From a3613ce43d1416a0c829d8a928b4cd7032df4686 Mon Sep 17 00:00:00 2001 From: Andreas Tille Date: Fri, 8 Jan 2021 10:47:39 +0100 Subject: [PATCH] Import fermi-lite_0.1+git20190320.b499514.orig.tar.xz [dgit import orig fermi-lite_0.1+git20190320.b499514.orig.tar.xz] --- .gitignore | 3 + LICENSE.txt | 23 ++ Makefile | 46 ++++ README.md | 96 +++++++ bfc.c | 674 +++++++++++++++++++++++++++++++++++++++++++++ bseq.c | 61 ++++ bubble.c | 366 ++++++++++++++++++++++++ example.c | 50 ++++ fml.h | 193 +++++++++++++ htab.c | 132 +++++++++ htab.h | 23 ++ internal.h | 21 ++ khash.h | 614 +++++++++++++++++++++++++++++++++++++++++ kmer.h | 106 +++++++ kseq.h | 248 +++++++++++++++++ ksort.h | 309 +++++++++++++++++++++ kstring.h | 169 ++++++++++++ ksw.c | 352 +++++++++++++++++++++++ ksw.h | 71 +++++ kthread.c | 65 +++++ kvec.h | 110 ++++++++ mag.c | 620 +++++++++++++++++++++++++++++++++++++++++ mag.h | 69 +++++ misc.c | 302 ++++++++++++++++++++ mrope.c | 307 +++++++++++++++++++++ mrope.h | 114 ++++++++ rld0.c | 489 ++++++++++++++++++++++++++++++++ rld0.h | 137 +++++++++ rle.c | 191 +++++++++++++ rle.h | 77 ++++++ rope.c | 219 +++++++++++++++ rope.h | 54 ++++ test/MT-simu.fq.gz | Bin 0 -> 185887 bytes unitig.c | 455 ++++++++++++++++++++++++++++++ 34 files changed, 6766 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 Makefile create mode 100644 README.md create mode 100644 bfc.c create mode 100644 bseq.c create mode 100644 bubble.c create mode 100644 example.c create mode 100644 fml.h create mode 100644 htab.c create mode 100644 htab.h create mode 100644 internal.h create mode 100644 khash.h create mode 100644 kmer.h create mode 100644 kseq.h create mode 100644 ksort.h create mode 100644 kstring.h create mode 100644 ksw.c create mode 100644 ksw.h create mode 100644 kthread.c create mode 100644 kvec.h create mode 100644 mag.c create mode 100644 mag.h create mode 100644 misc.c create mode 100644 mrope.c create mode 100644 mrope.h create mode 100644 rld0.c create mode 100644 rld0.h create mode 100644 rle.c create mode 100644 rle.h create mode 100644 rope.c create mode 100644 rope.h create mode 100644 test/MT-simu.fq.gz create mode 100644 unitig.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..282c932 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.a +.*.swp diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d423a43 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,23 @@ +The MIT License + +Copyright (c) 2016 Broad Institute + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..edda713 --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +CC= gcc +CFLAGS= -g -Wall -O2 -Wno-unused-function #-fno-inline-functions -fno-inline-functions-called-once +CPPFLAGS= +INCLUDES= +OBJS= kthread.o misc.o \ + bseq.o htab.o bfc.o \ + rle.o rope.o mrope.o rld0.o \ + unitig.o mag.o bubble.o ksw.o +PROG= fml-asm +LIBS= -lm -lz -lpthread + +.SUFFIXES:.c .o + +.c.o: + $(CC) -c $(CFLAGS) $(CPPFLAGS) $(INCLUDES) $< -o $@ + +all:$(PROG) + +fml-asm:libfml.a example.o + $(CC) $(CFLAGS) $^ -o $@ -L. -lfml $(LIBS) + +libfml.a:$(OBJS) + $(AR) -csru $@ $(OBJS) + +clean: + rm -fr gmon.out *.o ext/*.o a.out $(PROG) *~ *.a *.dSYM session* + +depend: + (LC_ALL=C; export LC_ALL; makedepend -Y -- $(CFLAGS) $(DFLAGS) -- *.c) + +# DO NOT DELETE + +bfc.o: htab.h kmer.h internal.h fml.h kvec.h ksort.h +bseq.o: fml.h kseq.h +bubble.o: mag.h kstring.h fml.h kvec.h ksw.h internal.h khash.h +example.o: fml.h +htab.o: htab.h kmer.h khash.h +ksw.o: ksw.h +mag.o: mag.h kstring.h fml.h kvec.h internal.h kseq.h khash.h ksort.h +misc.o: internal.h fml.h kstring.h rle.h mrope.h rope.h rld0.h mag.h kvec.h +misc.o: htab.h kmer.h khash.h +mrope.o: mrope.h rope.h +rld0.o: rld0.h +rle.o: rle.h +rope.o: rle.h rope.h +unitig.o: kvec.h kstring.h rld0.h mag.h fml.h internal.h ksort.h diff --git a/README.md b/README.md new file mode 100644 index 0000000..6dc7ff9 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +## Getting Started +```sh +git clone https://github.com/lh3/fermi-lite +cd fermi-lite && make +./fml-asm test/MT-simu.fq.gz > MT.fq +# to compile your program: +gcc -Wall -O2 prog.c -o prog -L/path/to/fermi-lite -lfml -lz -lm -lpthread +``` + +## Introduction + +Fermi-lite is a standalone C library as well as a command-line tool for +assembling Illumina short reads in regions from 100bp to 10 million bp in size. +It is largely a light-weight in-memory version of [fermikit][fk] without +generating any intermediate files. It inherits the performance, the relatively +small memory footprint and the features of fermikit. In particular, fermi-lite +is able to retain heterozygous events and thus can be used to assemble diploid +regions for the purpose of variant calling. It is one of the limited choices +for local re-assembly and arguably the easiest to interface. + +If you use fermi-lite in your work, please cite the FermiKit paper: + +> Li H (2015) FermiKit: assembly-based variant calling for Illumina +> resequencing data, *Bioinformatics*, **31**:3694-6. + +## Usage + +For now, see [example.c][example] for the basic use of the library. Here is a +sketch of the example: +```cpp +#include // for printf() +#include "fml.h" // only one header file required + +int main(int argc, char *argv[]) +{ + int i, n_seqs, n_utgs; + bseq1_t *seqs; // array of input sequences + fml_utg_t *utgs; // array of output unitigs + fml_opt_t opt; + if (argc == 1) return 1; // do nothing if there is no input file + seqs = bseq_read(argv[1], &n_seqs); // or fill the array with callers' functions + fml_opt_init(&opt); // initialize parameters + utgs = fml_assemble(&opt, n_seqs, seqs, &n_utgs); // assemble! + for (i = 0; i < n_utgs; ++i) // output in fasta + printf(">%d\n%s\n", i+1, utgs[i].seq); + fml_utg_destroy(n_utgs, utgs); // deallocate unitigs + return 0; +} +``` +The `fml_assemble()` output is in fact a graph. You may have a look at the +`fml_utg_print_gfa()` function in [misc.c][misc] about how to derive a +[GFA][gfa] representation from an array of `fml_utg_t` objects. + +## Overview of the Assembly Algorithm + +Fermi-lite is an overlap-based assembler. Given a set of input reads, it counts +*k*-mers, estimates the *k*-mer coverage, sets a threshold on *k*-mer +occurrences to determine solid *k*-mers and then use them correct sequencing +errors ([Li, 2015][bfc-paper]). After error correction, fermi-lite trims a read +at an *l*-mer unique to the read. It then constructs an FM-index for trimmed +reads ([Li, 2014][rb2-paper]) and builds a transitively reduced overlap graph from the +FM-index ([Simpson and Durbin, 2010][sga-paper]; [Li, 2012][fm1-paper]), +requiring at least *l*-bp overlaps. In this graph, fermi-lite trims tips and +pops bubbles caused by uncorrected errors. If a sequence in the graph has +multiple overlaps, fermi-lite discards overlaps significantly shorter than the +longest overlap -- this is a technique applied to overlap graph only. The graph +after these procedure is the final output. Sequences in this graph are unitigs. + +## Limitations + +1. Fermi-lite can efficiently assemble bacterial genomes. However, it has not + been carefully tuned for this type of assembly. While on a few GAGE-B data + sets fermi-lite appears to work well, it may not compete with recent + mainstream assemblers in general. + +2. Fermi-lite does not work with genomes more than tens of megabases as a + whole. It would take too much memory to stage all data in memory. For large + genomes, please use [fermikit][fk] instead. + +3. This is the first iteration of fermi-lite. It is still immarture. In + particular, I hope fermi-lite can be smart enough to automatically figure + out various parameters based on input, which is very challenging given the + high variability of input data. + +[sga-paper]: http://www.ncbi.nlm.nih.gov/pubmed/20529929 +[bfc-paper]: http://www.ncbi.nlm.nih.gov/pubmed/25953801 +[rb2-paper]: http://www.ncbi.nlm.nih.gov/pubmed/25107872 +[fm1-paper]: http://www.ncbi.nlm.nih.gov/pubmed/22569178 +[bfc]: http://github.com/lh3/bfc +[rb2]: http://github.com/lh3/ropebwt2 +[fm2]: http://github.com/lh3/fermi2 +[fk]: http://github.com/lh3/fermikit +[example]: https://github.com/lh3/fermi-lite/blob/master/example.c +[header]: https://github.com/lh3/fermi-lite/blob/master/fml.h +[misc]: https://github.com/lh3/fermi-lite/blob/master/misc.c +[gfa]: https://github.com/pmelsted/GFA-spec diff --git a/bfc.c b/bfc.c new file mode 100644 index 0000000..27ccfde --- /dev/null +++ b/bfc.c @@ -0,0 +1,674 @@ +#include +#include +#include +#include +#include +#include "htab.h" +#include "kmer.h" +#include "internal.h" +#include "fml.h" + +/******************* + *** BFC options *** + *******************/ + +typedef struct { + int n_threads, q, k, l_pre; + int min_cov; // a k-mer is considered solid if the count is no less than this + + int max_end_ext; + int win_multi_ec; + float min_trim_frac; + + // these ec options cannot be changed on the command line + int w_ec, w_ec_high, w_absent, w_absent_high; + int max_path_diff, max_heap; +} bfc_opt_t; + +void bfc_opt_init(bfc_opt_t *opt) +{ + memset(opt, 0, sizeof(bfc_opt_t)); + opt->n_threads = 1; + opt->q = 20; + opt->k = -1; + opt->l_pre = -1; + + opt->min_cov = 4; // in BFC, this defaults to 3 because it has Bloom pre-filter + opt->win_multi_ec = 10; + opt->max_end_ext = 5; + opt->min_trim_frac = .8; + + opt->w_ec = 1; + opt->w_ec_high = 7; + opt->w_absent = 3; + opt->w_absent_high = 1; + opt->max_path_diff = 15; + opt->max_heap = 100; +} + +/********************** + *** K-mer counting *** + **********************/ + +#define CNT_BUF_SIZE 256 + +typedef struct { // cache to reduce locking + uint64_t y[2]; + int is_high; +} insbuf_t; + +typedef struct { + int k, q; + int n_seqs; + const bseq1_t *seqs; + bfc_ch_t *ch; + int *n_buf; + insbuf_t **buf; +} cnt_step_t; + +bfc_kmer_t bfc_kmer_null = {{0,0,0,0}}; + +static int bfc_kmer_bufclear(cnt_step_t *cs, int forced, int tid) +{ + int i, k, r; + if (cs->ch == 0) return 0; + for (i = k = 0; i < cs->n_buf[tid]; ++i) { + r = bfc_ch_insert(cs->ch, cs->buf[tid][i].y, cs->buf[tid][i].is_high, forced); + if (r < 0) cs->buf[tid][k++] = cs->buf[tid][i]; + } + cs->n_buf[tid] = k; + return k; +} + +static void bfc_kmer_insert(cnt_step_t *cs, const bfc_kmer_t *x, int is_high, int tid) +{ + int k = cs->k; + uint64_t y[2], hash; + hash = bfc_kmer_hash(k, x->x, y); + if (bfc_ch_insert(cs->ch, y, is_high, 0) < 0) { + insbuf_t *p; + if (bfc_kmer_bufclear(cs, 0, tid) == CNT_BUF_SIZE) + bfc_kmer_bufclear(cs, 1, tid); + p = &cs->buf[tid][cs->n_buf[tid]++]; + p->y[0] = y[0], p->y[1] = y[1], p->is_high = is_high; + } +} + +static void worker_count(void *_data, long k, int tid) +{ + cnt_step_t *cs = (cnt_step_t*)_data; + const bseq1_t *s = &cs->seqs[k]; + int i, l; + bfc_kmer_t x = bfc_kmer_null; + uint64_t qmer = 0, mask = (1ULL<k) - 1; + for (i = l = 0; i < s->l_seq; ++i) { + int c = seq_nt6_table[(uint8_t)s->seq[i]] - 1; + if (c < 4) { + bfc_kmer_append(cs->k, x.x, c); + qmer = (qmer<<1 | (s->qual == 0 || s->qual[i] - 33 >= cs->q)) & mask; + if (++l >= cs->k) bfc_kmer_insert(cs, &x, (qmer == mask), tid); + } else l = 0, qmer = 0, x = bfc_kmer_null; + } +} + +struct bfc_ch_s *fml_count(int n, const bseq1_t *seq, int k, int q, int l_pre, int n_threads) +{ + int i; + cnt_step_t cs; + cs.n_seqs = n, cs.seqs = seq, cs.k = k, cs.q = q; + cs.ch = bfc_ch_init(cs.k, l_pre); + cs.n_buf = calloc(n_threads, sizeof(int)); + cs.buf = calloc(n_threads, sizeof(void*)); + for (i = 0; i < n_threads; ++i) + cs.buf[i] = malloc(CNT_BUF_SIZE * sizeof(insbuf_t)); + kt_for(n_threads, worker_count, &cs, cs.n_seqs); + for (i = 0; i < n_threads; ++i) free(cs.buf[i]); + free(cs.buf); free(cs.n_buf); + return cs.ch; +} + +/*************** + *** Correct *** + ***************/ + +#define BFC_MAX_KMER 63 +#define BFC_MAX_BF_SHIFT 37 + +#define BFC_MAX_PATHS 4 +#define BFC_EC_HIST 5 +#define BFC_EC_HIST_HIGH 2 + +#define BFC_EC_MIN_COV_COEF .1 + +/************************** + * Sequence struct for ec * + **************************/ + +#include "kvec.h" + +typedef struct { // NOTE: unaligned memory + uint8_t b:3, q:1, ob:3, oq:1; + uint8_t dummy; + uint16_t lcov:6, hcov:6, solid_end:1, high_end:1, ec:1, absent:1; + int i; +} ecbase_t; + +typedef kvec_t(ecbase_t) ecseq_t; + +static int bfc_seq_conv(const char *s, const char *q, int qthres, ecseq_t *seq) +{ + int i, l; + l = strlen(s); + kv_resize(ecbase_t, *seq, l); + seq->n = l; + for (i = 0; i < l; ++i) { + ecbase_t *c = &seq->a[i]; + c->b = c->ob = seq_nt6_table[(int)s[i]] - 1; + c->q = c->oq = !q? 1 : q[i] - 33 >= qthres? 1 : 0; + if (c->b > 3) c->q = c->oq = 0; + c->i = i; + } + return l; +} + +static inline ecbase_t ecbase_comp(const ecbase_t *b) +{ + ecbase_t r = *b; + r.b = b->b < 4? 3 - b->b : 4; + r.ob = b->ob < 4? 3 - b->ob : 4; + return r; +} + +static void bfc_seq_revcomp(ecseq_t *seq) +{ + int i; + for (i = 0; i < seq->n>>1; ++i) { + ecbase_t tmp; + tmp = ecbase_comp(&seq->a[i]); + seq->a[i] = ecbase_comp(&seq->a[seq->n - 1 - i]); + seq->a[seq->n - 1 - i] = tmp; + } + if (seq->n&1) seq->a[i] = ecbase_comp(&seq->a[i]); +} + +/*************************** + * Independent ec routines * + ***************************/ + +int bfc_ec_greedy_k(int k, int mode, const bfc_kmer_t *x, const bfc_ch_t *ch) +{ + int i, j, max = 0, max_ec = -1, max2 = 0; + for (i = 0; i < k; ++i) { + int c = (x->x[1]>>i&1)<<1 | (x->x[0]>>i&1); + for (j = 0; j < 4; ++j) { + bfc_kmer_t y = *x; + int ret; + if (j == c) continue; + bfc_kmer_change(k, y.x, i, j); + ret = bfc_ch_kmer_occ(ch, &y); + if (ret < 0) continue; + if ((max&0xff) < (ret&0xff)) max2 = max, max = ret, max_ec = i<<2 | j; + else if ((max2&0xff) < (ret&0xff)) max2 = ret; + } + } + return (max&0xff) * 3 > mode && (max2&0xff) < 3? max_ec : -1; +} + +int bfc_ec_first_kmer(int k, const ecseq_t *s, int start, bfc_kmer_t *x) +{ + int i, l; + *x = bfc_kmer_null; + for (i = start, l = 0; i < s->n; ++i) { + ecbase_t *c = &s->a[i]; + if (c->b < 4) { + bfc_kmer_append(k, x->x, c->b); + if (++l == k) break; + } else l = 0, *x = bfc_kmer_null; + } + return i; +} + +void bfc_ec_kcov(int k, int min_occ, ecseq_t *s, const bfc_ch_t *ch) +{ + int i, l, r, j; + bfc_kmer_t x = bfc_kmer_null; + for (i = l = 0; i < s->n; ++i) { + ecbase_t *c = &s->a[i]; + c->high_end = c->solid_end = c->lcov = c->hcov = 0; + if (c->b < 4) { + bfc_kmer_append(k, x.x, c->b); + if (++l >= k) { + if ((r = bfc_ch_kmer_occ(ch, &x)) >= 0) { + if ((r>>8&0x3f) >= min_occ+1) c->high_end = 1; + if ((r&0xff) >= min_occ) { + c->solid_end = 1; + for (j = i - k + 1; j <= i; ++j) + ++s->a[j].lcov, s->a[j].hcov += c->high_end; + } + } + } + } else l = 0, x = bfc_kmer_null; + } +} + +uint64_t bfc_ec_best_island(int k, const ecseq_t *s) +{ // IMPORTANT: call bfc_ec_kcov() before calling this function! + int i, l, max, max_i; + for (i = k - 1, max = l = 0, max_i = -1; i < s->n; ++i) { + if (!s->a[i].solid_end) { + if (l > max) max = l, max_i = i; + l = 0; + } else ++l; + } + if (l > max) max = l, max_i = i; + return max > 0? (uint64_t)(max_i - max - k + 1) << 32 | max_i : 0; +} + +/******************** + * Correct one read * + ********************/ + +#include "ksort.h" + +#define ECCODE_MISC 1 +#define ECCODE_MANY_N 2 +#define ECCODE_NO_SOLID 3 +#define ECCODE_UNCORR_N 4 +#define ECCODE_MANY_FAIL 5 + +typedef struct { + uint32_t ec_code:3, brute:1, n_ec:14, n_ec_high:14; + uint32_t n_absent:24, max_heap:8; +} ecstat_t; + +typedef struct { + uint8_t ec:1, ec_high:1, absent:1, absent_high:1, b:4; +} bfc_penalty_t; + +typedef struct { + int tot_pen; + int i; // base position + int k; // position in the stack + int32_t ecpos_high[BFC_EC_HIST_HIGH]; + int32_t ecpos[BFC_EC_HIST]; + bfc_kmer_t x; +} echeap1_t; + +typedef struct { + int parent, i, tot_pen; + uint8_t b; + bfc_penalty_t pen; + uint16_t cnt; +} ecstack1_t; + +typedef struct { + const bfc_opt_t *opt; + const bfc_ch_t *ch; + kvec_t(echeap1_t) heap; + kvec_t(ecstack1_t) stack; + ecseq_t seq, tmp, ec[2]; + int mode; + ecstat_t ori_st; +} bfc_ec1buf_t; + +#define heap_lt(a, b) ((a).tot_pen > (b).tot_pen) +KSORT_INIT(ec, echeap1_t, heap_lt) + +static bfc_ec1buf_t *ec1buf_init(const bfc_opt_t *opt, const bfc_ch_t *ch) +{ + bfc_ec1buf_t *e; + e = calloc(1, sizeof(bfc_ec1buf_t)); + e->opt = opt, e->ch = ch; + return e; +} + +static void ec1buf_destroy(bfc_ec1buf_t *e) +{ + free(e->heap.a); free(e->stack.a); free(e->seq.a); free(e->tmp.a); free(e->ec[0].a); free(e->ec[1].a); + free(e); +} + +#define weighted_penalty(o, p) ((o)->w_ec * (p).ec + (o)->w_ec_high * (p).ec_high + (o)->w_absent * (p).absent + (o)->w_absent_high * (p).absent_high) + +static void buf_update(bfc_ec1buf_t *e, const echeap1_t *prev, bfc_penalty_t pen, int cnt) +{ + ecstack1_t *q; + echeap1_t *r; + const bfc_opt_t *o = e->opt; + int b = pen.b; + // update stack + kv_pushp(ecstack1_t, e->stack, &q); + q->parent = prev->k; + q->i = prev->i; + q->b = b; + q->pen = pen; + q->cnt = cnt > 0? cnt&0xff : 0; + q->tot_pen = prev->tot_pen + weighted_penalty(o, pen); + // update heap + kv_pushp(echeap1_t, e->heap, &r); + r->i = prev->i + 1; + r->k = e->stack.n - 1; + r->x = prev->x; + if (pen.ec_high) { + memcpy(r->ecpos_high + 1, prev->ecpos_high, (BFC_EC_HIST_HIGH - 1) * 4); + r->ecpos_high[0] = prev->i; + } else memcpy(r->ecpos_high, prev->ecpos_high, BFC_EC_HIST_HIGH * 4); + if (pen.ec) { + memcpy(r->ecpos + 1, prev->ecpos, (BFC_EC_HIST - 1) * 4); + r->ecpos[0] = prev->i; + } else memcpy(r->ecpos, prev->ecpos, BFC_EC_HIST * 4); + r->tot_pen = q->tot_pen; + bfc_kmer_append(e->opt->k, r->x.x, b); + ks_heapup_ec(e->heap.n, e->heap.a); +} + +static int buf_backtrack(ecstack1_t *s, int end, const ecseq_t *seq, ecseq_t *path) +{ + int i, n_absent = 0; + kv_resize(ecbase_t, *path, seq->n); + path->n = seq->n; + while (end >= 0) { + if ((i = s[end].i) < seq->n) { + path->a[i].b = s[end].b; + path->a[i].ec = s[end].pen.ec; + path->a[i].absent = s[end].pen.absent; + n_absent += s[end].pen.absent; + } + end = s[end].parent; + } + return n_absent; +} + +static int bfc_ec1dir(bfc_ec1buf_t *e, const ecseq_t *seq, ecseq_t *ec, int start, int end, int *max_heap) +{ + echeap1_t z; + int i, l, rv = -1, path[BFC_MAX_PATHS], n_paths = 0, min_path = -1, min_path_pen = INT_MAX, n_failures = 0; + assert(end <= seq->n && end - start >= e->opt->k); + e->heap.n = e->stack.n = 0; + *max_heap = 0; + memset(&z, 0, sizeof(echeap1_t)); + kv_resize(ecbase_t, *ec, seq->n); + ec->n = seq->n; + for (z.i = start, l = 0; z.i < end; ++z.i) { + int c = seq->a[z.i].b; + if (c < 4) { + if (++l == e->opt->k) break; + bfc_kmer_append(e->opt->k, z.x.x, c); + } else l = 0, z.x = bfc_kmer_null; + } + assert(z.i < end); // before calling this function, there must be at least one solid k-mer + z.k = -1; + for (i = 0; i < BFC_EC_HIST; ++i) z.ecpos[i] = -1; + for (i = 0; i < BFC_EC_HIST_HIGH; ++i) z.ecpos_high[i] = -1; + kv_push(echeap1_t, e->heap, z); + for (i = 0; i < seq->n; ++i) ec->a[i].b = seq->a[i].b, ec->a[i].ob = seq->a[i].ob; + // exhaustive error correction + while (1) { + int stop = 0; + *max_heap = *max_heap > 255? 255 : *max_heap > e->heap.n? *max_heap : e->heap.n; + if (e->heap.n == 0) { // may happen when there is an uncorrectable "N" + rv = -2; + break; + } + z = e->heap.a[0]; + e->heap.a[0] = kv_pop(e->heap); + ks_heapdown_ec(0, e->heap.n, e->heap.a); + if (min_path >= 0 && z.tot_pen > min_path_pen + e->opt->max_path_diff) break; + if (z.i - end > e->opt->max_end_ext) stop = 1; + if (!stop) { + ecbase_t *c = z.i < seq->n? &seq->a[z.i] : 0; + int b, os = -1, fixed = 0, other_ext = 0, n_added = 0, added_cnt[4]; + bfc_penalty_t added[4]; + // test if the read extension alone is enough + if (z.i > end) fixed = 1; + if (c && c->b < 4) { // A, C, G or T + bfc_kmer_t x = z.x; + bfc_kmer_append(e->opt->k, x.x, c->b); + os = bfc_ch_kmer_occ(e->ch, &x); + if (c->q && (os&0xff) >= e->opt->min_cov + 1 && c->lcov >= e->opt->min_cov + 1) fixed = 1; + else if (c->hcov > e->opt->k * .75) fixed = 1; + } + // extension + for (b = 0; b < 4; ++b) { + bfc_penalty_t pen; + if (fixed && c && b != c->b) continue; + if (c == 0 || b != c->b) { + int s; + bfc_kmer_t x = z.x; + pen.ec = 0, pen.ec_high = 0, pen.absent = 0, pen.absent_high = 0, pen.b = b; + if (c) { // not over the end + if (c->q && z.ecpos_high[BFC_EC_HIST_HIGH-1] >= 0 && z.i - z.ecpos_high[BFC_EC_HIST_HIGH-1] < e->opt->win_multi_ec) continue; // no close highQ corrections + if (z.ecpos[BFC_EC_HIST-1] >= 0 && z.i - z.ecpos[BFC_EC_HIST-1] < e->opt->win_multi_ec) continue; // no clustered corrections + } + bfc_kmer_append(e->opt->k, x.x, b); + s = bfc_ch_kmer_occ(e->ch, &x); + if (s < 0 || (s&0xff) < e->opt->min_cov) continue; // not solid + //if (os >= 0 && (s&0xff) - (os&0xff) < 2) continue; // not sufficiently better than the read path + pen.ec = c && c->b < 4? 1 : 0; + pen.ec_high = pen.ec? c->oq : 0; + pen.absent = 0; + pen.absent_high = ((s>>8&0xff) < e->opt->min_cov); + pen.b = b; + added_cnt[n_added] = s; + added[n_added++] = pen; + ++other_ext; + } else { + pen.ec = pen.ec_high = 0; + pen.absent = (os < 0 || (os&0xff) < e->opt->min_cov); + pen.absent_high = (os < 0 || (os>>8&0xff) < e->opt->min_cov); + pen.b = b; + added_cnt[n_added] = os; + added[n_added++] = pen; + } + } // ~for(b) + if (fixed == 0 && other_ext == 0) ++n_failures; + if (n_failures > seq->n * 2) { + rv = -3; + break; + } + if (c || n_added == 1) { + if (n_added > 1 && e->heap.n > e->opt->max_heap) { // to prevent heap explosion + int min_b = -1, min = INT_MAX; + for (b = 0; b < n_added; ++b) { + int t = weighted_penalty(e->opt, added[b]); + if (min > t) min = t, min_b = b; + } + buf_update(e, &z, added[min_b], added_cnt[min_b]); + } else { + for (b = 0; b < n_added; ++b) + buf_update(e, &z, added[b], added_cnt[b]); + } + } else { + if (n_added == 0) + e->stack.a[z.k].tot_pen += e->opt->w_absent * (e->opt->max_end_ext - (z.i - end)); + stop = 1; + } + } // ~if(!stop) + if (stop) { + if (e->stack.a[z.k].tot_pen < min_path_pen) + min_path_pen = e->stack.a[z.k].tot_pen, min_path = n_paths; + path[n_paths++] = z.k; + if (n_paths == BFC_MAX_PATHS) break; + } + } // ~while(1) + // backtrack + if (n_paths == 0) return rv; + assert(min_path >= 0 && min_path < n_paths && e->stack.a[path[min_path]].tot_pen == min_path_pen); + rv = buf_backtrack(e->stack.a, path[min_path], seq, ec); + for (i = 0; i < ec->n; ++i) // mask out uncorrected regions + if (i < start + e->opt->k || i >= end) ec->a[i].b = 4; + return rv; +} + +ecstat_t bfc_ec1(bfc_ec1buf_t *e, char *seq, char *qual) +{ + int i, start = 0, end = 0, n_n = 0, rv[2], max_heap[2]; + uint64_t r; + ecstat_t s; + + s.ec_code = ECCODE_MISC, s.brute = 0, s.n_ec = s.n_ec_high = 0, s.n_absent = s.max_heap = 0; + bfc_seq_conv(seq, qual, e->opt->q, &e->seq); + for (i = 0; i < e->seq.n; ++i) + if (e->seq.a[i].ob > 3) ++n_n; + if (n_n > e->seq.n * .05) { + s.ec_code = ECCODE_MANY_N; + return s; + } + bfc_ec_kcov(e->opt->k, e->opt->min_cov, &e->seq, e->ch); + r = bfc_ec_best_island(e->opt->k, &e->seq); + if (r == 0) { // no solid k-mer + bfc_kmer_t x; + int ec = -1; + while ((end = bfc_ec_first_kmer(e->opt->k, &e->seq, start, &x)) < e->seq.n) { + ec = bfc_ec_greedy_k(e->opt->k, e->mode, &x, e->ch); + if (ec >= 0) break; + if (end + (e->opt->k>>1) >= e->seq.n) break; + start = end - (e->opt->k>>1); + } + if (ec >= 0) { + e->seq.a[end - (ec>>2)].b = ec&3; + ++end; start = end - e->opt->k; + s.brute = 1; + } else { + s.ec_code = ECCODE_NO_SOLID; + return s; + } + } else start = r>>32, end = (uint32_t)r; + if ((rv[0] = bfc_ec1dir(e, &e->seq, &e->ec[0], start, e->seq.n, &max_heap[0])) < 0) { + s.ec_code = rv[0] == -2? ECCODE_UNCORR_N : rv[0] == -3? ECCODE_MANY_FAIL : ECCODE_MISC; + return s; + } + bfc_seq_revcomp(&e->seq); + if ((rv[1] = bfc_ec1dir(e, &e->seq, &e->ec[1], e->seq.n - end, e->seq.n, &max_heap[1])) < 0) { + s.ec_code = rv[1] == -2? ECCODE_UNCORR_N : rv[1] == -3? ECCODE_MANY_FAIL : ECCODE_MISC; + return s; + } + s.max_heap = max_heap[0] > max_heap[1]? max_heap[0] : max_heap[1]; + s.ec_code = 0, s.n_absent = rv[0] + rv[1]; + bfc_seq_revcomp(&e->ec[1]); + bfc_seq_revcomp(&e->seq); + for (i = 0; i < e->seq.n; ++i) { + ecbase_t *c = &e->seq.a[i]; + if (e->ec[0].a[i].b == e->ec[1].a[i].b) + c->b = e->ec[0].a[i].b > 3? e->seq.a[i].b : e->ec[0].a[i].b; + else if (e->ec[1].a[i].b > 3) c->b = e->ec[0].a[i].b; + else if (e->ec[0].a[i].b > 3) c->b = e->ec[1].a[i].b; + else c->b = e->seq.a[i].ob; + } + for (i = 0; i < e->seq.n; ++i) { + int is_diff = !(e->seq.a[i].b == e->seq.a[i].ob); + if (is_diff) { + ++s.n_ec; + if (e->seq.a[i].q) ++s.n_ec_high; + } + seq[i] = (is_diff? "acgtn" : "ACGTN")[e->seq.a[i].b]; + if (qual) qual[i] = is_diff? 34 + e->seq.a[i].ob : "+?"[e->seq.a[i].q]; + } + return s; +} + +/******************** + * Error correction * + ********************/ + +typedef struct { + const bfc_opt_t *opt; + const bfc_ch_t *ch; + bfc_ec1buf_t **e; + int64_t n_processed; + int n_seqs, flt_uniq; + bseq1_t *seqs; +} ec_step_t; + +static uint64_t max_streak(int k, const bfc_ch_t *ch, const bseq1_t *s) +{ + int i, l; + uint64_t max = 0, t = 0; + bfc_kmer_t x = bfc_kmer_null; + for (i = l = 0; i < s->l_seq; ++i) { + int c = seq_nt6_table[(uint8_t)s->seq[i]] - 1; + if (c < 4) { // not an ambiguous base + bfc_kmer_append(k, x.x, c); + if (++l >= k) { // ok, we have a k-mer now + if (bfc_ch_kmer_occ(ch, &x) > 0) t += 1ULL<<32; + else t = i + 1; + } else t = i + 1; + } else l = 0, x = bfc_kmer_null, t = i + 1; + max = max > t? max : t; + } + return max; +} + +static void worker_ec(void *_data, long k, int tid) +{ + ec_step_t *es = (ec_step_t*)_data; + bseq1_t *s = &es->seqs[k]; + if (es->flt_uniq) { + uint64_t max; + max = max_streak(es->opt->k, es->ch, s); + if (max>>32 && (double)((max>>32) + es->opt->k - 1) / s->l_seq > es->opt->min_trim_frac) { + int start = (uint32_t)max, end = start + (max>>32); + start -= es->opt->k - 1; + assert(start >= 0 && end <= s->l_seq); + memmove(s->seq, s->seq + start, end - start); + s->l_seq = end - start; + s->seq[s->l_seq] = 0; + if (s->qual) { + memmove(s->qual, s->qual + start, s->l_seq); + s->qual[s->l_seq] = 0; + } + } else { + free(s->seq); free(s->qual); + s->l_seq = 0, s->seq = s->qual = 0; + } + } else bfc_ec1(es->e[tid], s->seq, s->qual); +} + +float fml_correct_core(const fml_opt_t *opt, int flt_uniq, int n, bseq1_t *seq) +{ + bfc_ch_t *ch; + int i, mode; + uint64_t hist[256], hist_high[64], tot_len = 0, sum_k = 0, tot_k = 0; + ec_step_t es; + bfc_opt_t bfc_opt; + float kcov; + + // initialize BFC options + bfc_opt_init(&bfc_opt); + bfc_opt.n_threads = opt->n_threads; // copy from FML options + bfc_opt.k = flt_uniq? opt->min_asm_ovlp : opt->ec_k; + for (i = 0; i < n; ++i) tot_len += seq[i].l_seq; // compute total length + bfc_opt.l_pre = tot_len - 8 < 20? tot_len - 8 : 20; + + memset(&es, 0, sizeof(ec_step_t)); + es.opt = &bfc_opt, es.n_seqs = n, es.seqs = seq, es.flt_uniq = flt_uniq; + + es.ch = ch = fml_count(n, seq, bfc_opt.k, bfc_opt.q, bfc_opt.l_pre, bfc_opt.n_threads); + mode = bfc_ch_hist(ch, hist, hist_high); + for (i = opt->min_cnt; i < 256; ++i) + sum_k += hist[i], tot_k += i * hist[i]; + kcov = (float)tot_k / sum_k; + bfc_opt.min_cov = (int)(BFC_EC_MIN_COV_COEF * kcov + .499); + bfc_opt.min_cov = bfc_opt.min_cov < opt->max_cnt? bfc_opt.min_cov : opt->max_cnt; + bfc_opt.min_cov = bfc_opt.min_cov > opt->min_cnt? bfc_opt.min_cov : opt->min_cnt; + + es.e = calloc(es.opt->n_threads, sizeof(void*)); + for (i = 0; i < es.opt->n_threads; ++i) + es.e[i] = ec1buf_init(es.opt, ch), es.e[i]->mode = mode; + kt_for(es.opt->n_threads, worker_ec, &es, es.n_seqs); + for (i = 0; i < es.opt->n_threads; ++i) + ec1buf_destroy(es.e[i]); + free(es.e); + bfc_ch_destroy(ch); + return kcov; +} + +float fml_correct(const fml_opt_t *opt, int n, bseq1_t *seq) +{ + return fml_correct_core(opt, 0, n, seq); +} + +float fml_fltuniq(const fml_opt_t *opt, int n, bseq1_t *seq) +{ + return fml_correct_core(opt, 1, n, seq); +} diff --git a/bseq.c b/bseq.c new file mode 100644 index 0000000..719bafb --- /dev/null +++ b/bseq.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include "fml.h" +#include "kseq.h" +KSEQ_INIT(gzFile, gzread) + +bseq1_t *bseq_read(const char *fn, int *n_) +{ + gzFile fp; + bseq1_t *seqs; + kseq_t *ks; + int m, n; + uint64_t size = 0; + + *n_ = 0; + fp = fn && strcmp(fn, "-")? gzopen(fn, "r") : gzdopen(fileno(stdin), "r"); + if (fp == 0) return 0; + ks = kseq_init(fp); + + m = n = 0; seqs = 0; + while (kseq_read(ks) >= 0) { + bseq1_t *s; + if (n >= m) { + m = m? m<<1 : 256; + seqs = realloc(seqs, m * sizeof(bseq1_t)); + } + s = &seqs[n]; + s->seq = strdup(ks->seq.s); + s->qual = ks->qual.l? strdup(ks->qual.s) : 0; + s->l_seq = ks->seq.l; + size += seqs[n++].l_seq; + } + *n_ = n; + + kseq_destroy(ks); + gzclose(fp); + return seqs; +} + +void seq_reverse(int l, unsigned char *s) +{ + int i; + for (i = 0; i < l>>1; ++i) { + int tmp = s[l-1-i]; + s[l-1-i] = s[i]; s[i] = tmp; + } +} + +void seq_revcomp6(int l, unsigned char *s) +{ + int i; + for (i = 0; i < l>>1; ++i) { + int tmp = s[l-1-i]; + tmp = (tmp >= 1 && tmp <= 4)? 5 - tmp : tmp; + s[l-1-i] = (s[i] >= 1 && s[i] <= 4)? 5 - s[i] : s[i]; + s[i] = tmp; + } + if (l&1) s[i] = (s[i] >= 1 && s[i] <= 4)? 5 - s[i] : s[i]; +} diff --git a/bubble.c b/bubble.c new file mode 100644 index 0000000..cbe54be --- /dev/null +++ b/bubble.c @@ -0,0 +1,366 @@ +#include +#include +#include "mag.h" +#include "kvec.h" +#include "ksw.h" +#include "internal.h" +#include "khash.h" +KHASH_DECLARE(64, uint64_t, uint64_t) + +typedef khash_t(64) hash64_t; + +#define MAX_N_DIFF 2.01 // for evaluating alignment after SW +#define MAX_R_DIFF 0.1 +#define L_DIFF_COEF 0.2 // n_diff=|l_0 - l_1|*L_DIFF_COEF + +#define edge_mark_del(_x) ((_x).x = (uint64_t)-2, (_x).y = 0) +#define edge_is_del(_x) ((_x).x == (uint64_t)-2 || (_x).y == 0) + +/****************** + * Closed bubbles * + ******************/ + +typedef struct { + uint64_t id; + int cnt[2]; + int n[2][2], d[2][2]; + uint64_t v[2][2]; +} trinfo_t; + +const trinfo_t g_trinull = {-1, {0, 0}, {{INT_MIN, INT_MIN}, {INT_MIN, INT_MIN}}, {{INT_MIN, INT_MIN}, {INT_MIN, INT_MIN}}, {{-1, -1}, {-1, -1}}}; + +typedef struct { + int n, m; + trinfo_t **buf; +} tipool_t; + +struct mogb_aux { + tipool_t pool; + ku64_v stack; + hash64_t *h; +}; + +mogb_aux_t *mag_b_initaux(void) +{ + mogb_aux_t *aux = calloc(1, sizeof(mogb_aux_t)); + aux->h = kh_init(64); + return aux; +} + +void mag_b_destroyaux(mogb_aux_t *b) +{ + int i; + for (i = 0; i < b->pool.m; ++i) + free(b->pool.buf[i]); + free(b->pool.buf); free(b->stack.a); + kh_destroy(64, b->h); + free(b); +} + +#define tiptr(p) ((trinfo_t*)(p)->ptr) + +static inline trinfo_t *tip_alloc(tipool_t *pool, uint32_t id) +{ // allocate an object from the memory pool + trinfo_t *p; + if (pool->n == pool->m) { + int i, new_m = pool->m? pool->m<<1 : 256; + pool->buf = realloc(pool->buf, new_m * sizeof(void*)); + for (i = pool->m; i < new_m; ++i) + pool->buf[i] = malloc(sizeof(trinfo_t)); + pool->m = new_m; + } + p = pool->buf[pool->n++]; + *p = g_trinull; + p->id = id; + return p; +} + +static void backtrace(mag_t *g, uint64_t end, uint64_t start, hash64_t *h) +{ + while (end>>32 != start) { + int ret; + kh_put(64, h, end>>33, &ret); + end = tiptr(&g->v.a[end>>33])->v[(end>>32^1)&1][end&1]; + } +} + +void mag_vh_simplify_bubble(mag_t *g, uint64_t idd, int max_vtx, int max_dist, mogb_aux_t *a) +{ + int i, n_pending = 0; + magv_t *p, *q; + + p = &g->v.a[idd>>1]; + if (p->len < 0 || p->nei[idd&1].n < 2) return; // stop if p is deleted or it has 0 or 1 neighbor + // reset aux data + a->stack.n = a->pool.n = 0; + if (kh_n_buckets(a->h) >= 64) { + kh_destroy(64, a->h); + a->h = kh_init(64); + } else kh_clear(64, a->h); + // add the initial vertex + p->ptr = tip_alloc(&a->pool, idd>>1); + tiptr(p)->d[(idd&1)^1][0] = -p->len; + tiptr(p)->n[(idd&1)^1][0] = -p->nsr; + kv_push(uint64_t, a->stack, idd^1); + // essentially a topological sorting + while (a->stack.n) { + uint64_t x, y; + ku128_v *r; + if (a->stack.n == 1 && a->stack.a[0] != (idd^1) && n_pending == 0) break; // found the other end of the bubble + x = kv_pop(a->stack); + p = &g->v.a[x>>1]; + //printf("%lld:%lld\n", p->k[0], p->k[1]); + r = &p->nei[(x&1)^1]; // we will look the the neighbors from the other end of the unitig + if (a->pool.n > max_vtx || tiptr(p)->d[x&1][0] > max_dist || tiptr(p)->d[x&1][1] > max_dist || r->n == 0) break; // we failed + // set the distance to p's neighbors + for (i = 0; i < r->n; ++i) { + int nsr, dist, which; + if ((int64_t)r->a[i].x < 0) continue; + y = mag_tid2idd(g->h, r->a[i].x); + if (y == (idd^1)) { // there is a loop involving the initial vertex + a->stack.n = 0; + break; // not a bubble; stop; this will jump out of the while() loop + } + q = &g->v.a[y>>1]; + if (q->ptr == 0) { // has not been attempted + q->ptr = tip_alloc(&a->pool, y>>1), ++n_pending; + mag_v128_clean(&q->nei[y&1]); // make sure there are no deleted edges + } + nsr = tiptr(p)->n[x&1][0] + p->nsr; which = 0; + dist = tiptr(p)->d[x&1][0] + p->len - r->a[i].y; + //printf("01 [%d]\t[%d,%d]\t[%d,%d]\n", i, tiptr(q)->n[y&1][0], tiptr(q)->n[y&1][1], tiptr(q)->d[y&1][0], tiptr(q)->d[y&1][1]); + // test and possibly update the tentative distance + if (nsr > tiptr(q)->n[y&1][0]) { // then move the best to the 2nd best and update the best + tiptr(q)->n[y&1][1] = tiptr(q)->n[y&1][0]; tiptr(q)->n[y&1][0] = nsr; + tiptr(q)->v[y&1][1] = tiptr(q)->v[y&1][0]; tiptr(q)->v[y&1][0] = (x^1)<<32|i<<1|which; + tiptr(q)->d[y&1][1] = tiptr(q)->d[y&1][0]; tiptr(q)->d[y&1][0] = dist; + nsr = tiptr(p)->n[x&1][1] + p->nsr; which = 1; // now nsr is the 2nd best + dist = tiptr(p)->d[x&1][1] + p->len - r->a[i].y; + } + if (nsr > tiptr(q)->n[y&1][1]) // update the 2nd best + tiptr(q)->n[y&1][1] = nsr, tiptr(q)->v[y&1][1] = (x^1)<<32|i<<1|which, tiptr(q)->d[y&1][1] = dist; + if (++tiptr(q)->cnt[y&1] == q->nei[y&1].n) { // all q's predecessors have been processed; then push + kv_push(uint64_t, a->stack, y); + --n_pending; + } + } + } + if (n_pending == 0 && a->stack.n == 1) { // found a bubble + uint64_t x = a->stack.a[0]; + p = &g->v.a[x>>1]; + //printf("(%d,%d)\t(%d,%d)\n", tiptr(p)->n[x&1][0], tiptr(p)->n[x&1][1], tiptr(p)->d[x&1][0], tiptr(p)->d[x&1][1]); + backtrace(g, tiptr(p)->v[x&1][0], idd, a->h); + backtrace(g, tiptr(p)->v[x&1][1], idd, a->h); + } + for (i = 0; i < a->pool.n; ++i) // reset p->ptr + g->v.a[a->pool.buf[i]->id].ptr = 0; + if (kh_size(a->h)) { // bubble detected; then remove verticies not in the top two paths + for (i = 1; i < a->pool.n; ++i) { // i=0 corresponds to the initial vertex which we want to exclude + uint64_t id = a->pool.buf[i]->id; + if (id != a->stack.a[0]>>1 && kh_get(64, a->h, id) == kh_end(a->h)) // not in the top two paths + mag_v_del(g, &g->v.a[id]); + } + } +} + +void mag_g_simplify_bubble(mag_t *g, int max_vtx, int max_dist) +{ + int64_t i; + mogb_aux_t *a; + a = mag_b_initaux(); + for (i = 0; i < g->v.n; ++i) { + mag_vh_simplify_bubble(g, i<<1|0, max_vtx, max_dist, a); + mag_vh_simplify_bubble(g, i<<1|1, max_vtx, max_dist, a); + } + mag_b_destroyaux(a); + mag_g_merge(g, 0, 0); +} + +int mag_vh_pop_simple(mag_t *g, uint64_t idd, float max_cov, float max_frac, int max_bdiff, int aggressive) +{ + magv_t *p = &g->v.a[idd>>1], *q[2]; + ku128_v *r; + int i, j, k, dir[2], l[2], ret = -1; + char *seq[2], *cov[2]; + float n_diff, r_diff, avg[2], max_n_diff = aggressive? MAX_N_DIFF * 2. : MAX_N_DIFF; + + if (p->len < 0 || p->nei[idd&1].n != 2) return ret; // deleted or no bubble + r = &p->nei[idd&1]; + for (j = 0; j < 2; ++j) { + uint64_t x; + if ((int64_t)r->a[j].x < 0) return ret; + x = mag_tid2idd(g->h, r->a[j].x); + dir[j] = x&1; + q[j] = &g->v.a[x>>1]; + if (q[j]->nei[0].n != 1 || q[j]->nei[1].n != 1) return ret; // no bubble + l[j] = q[j]->len - (int)(q[j]->nei[0].a->y + q[j]->nei[1].a->y); // bubble length, excluding overlaps + } + if (q[0]->nei[dir[0]^1].a->x != q[1]->nei[dir[1]^1].a->x) return ret; // no bubble + if (l[0] - l[1] > max_bdiff || l[1] - l[0] > max_bdiff) return 1; // huge bubble differences + for (j = 0; j < 2; ++j) { // set seq[] and cov[], and compute avg[] + if (l[j] > 0) { + seq[j] = malloc(l[j]<<1); + cov[j] = seq[j] + l[j]; + for (i = 0; i < l[j]; ++i) { + seq[j][i] = q[j]->seq[i + q[j]->nei[0].a->y]; + cov[j][i] = q[j]->cov[i + q[j]->nei[0].a->y]; + } + if (dir[j]) { + seq_revcomp6(l[j], (uint8_t*)seq[j]); + seq_reverse(l[j], (uint8_t*)cov[j]); + } + for (i = 0, avg[j] = 0.; i < l[j]; ++i) { + --seq[j][i]; // change DNA6 encoding to DNA4 for SW below + avg[j] += cov[j][i] - 33; + } + avg[j] /= l[j]; + } else { // l[j] <= 0; this may happen around a tandem repeat + int beg, end; + seq[j] = cov[j] = 0; + beg = q[j]->nei[0].a->y; end = q[j]->len - q[j]->nei[1].a->y; + if (beg > end) beg ^= end, end ^= beg, beg ^= end; // swap + if (beg < end) { + for (i = beg, avg[j] = 0.; i < end; ++i) + avg[j] += q[j]->cov[i] - 33; + avg[j] /= end - beg; + } else avg[j] = q[j]->cov[beg] - 33; // FIXME: when q[j] is contained, weird thing may happen + } + } + ret = 1; + if (l[0] > 0 && l[1] > 0) { // then do SW to compute n_diff and r_diff + int8_t mat[16]; + kswr_t aln; + for (i = k = 0; i < 4; ++i) + for (j = 0; j < 4; ++j) + mat[k++] = i == j? 5 : -4; + aln = ksw_align(l[0], (uint8_t*)seq[0], l[1], (uint8_t*)seq[1], 4, mat, 5, 2, 0, 0); + n_diff = ((l[0] < l[1]? l[0] : l[1]) * 5. - aln.score) / (5. + 4.); // 5: matching score; -4: mismatchig score + r_diff = n_diff / ((l[0] + l[1]) / 2.); + //fprintf(stderr, "===> %f %f <===\n", n_diff, r_diff); for (j = 0; j < 2; ++j) { for (i = 0; i < l[j]; ++i) fputc("ACGTN"[(int)seq[j][i]], stderr); fputc('\n', stderr); } + } else { + n_diff = abs(l[0] - l[1]) * L_DIFF_COEF; + r_diff = 1.; + //fprintf(stderr, "---> (%d,%d) <---\n", l[0], l[1]); + } + if (n_diff < max_n_diff || r_diff < MAX_R_DIFF) { + j = avg[0] < avg[1]? 0 : 1; + if (aggressive || (avg[j] < max_cov && avg[j] / (avg[j^1] + avg[j]) < max_frac)) { + mag_v_del(g, q[j]); + ret = 2; + } + } + free(seq[0]); free(seq[1]); + return ret; +} + +void mag_g_pop_simple(mag_t *g, float max_cov, float max_frac, int min_merge_len, int max_bdiff, int aggressive) +{ + int64_t i, n_examined = 0, n_popped = 0; + int ret; + + for (i = 0; i < g->v.n; ++i) { + ret = mag_vh_pop_simple(g, i<<1|0, max_cov, max_frac, max_bdiff, aggressive); + if (ret >= 1) ++n_examined; + if (ret >= 2) ++n_popped; + ret = mag_vh_pop_simple(g, i<<1|1, max_cov, max_frac, max_bdiff, aggressive); + if (ret >= 1) ++n_examined; + if (ret >= 2) ++n_popped; + } + if (fm_verbose >= 3) + fprintf(stderr, "[M::%s] examined %ld bubbles and popped %ld\n", __func__, (long)n_examined, (long)n_popped); + mag_g_merge(g, 0, min_merge_len); +} + +/**************** + * Open bubbles * + ****************/ + +void mag_v_pop_open(mag_t *g, magv_t *p, int min_elen) +{ + int i, j, k, l, dir, max_l, l_qry; + magv_t *q, *t; + ku128_v *r, *s; + uint8_t *seq; + int8_t mat[16]; + + if (p->len < 0 || p->len >= min_elen) return; + //if (p->nei[0].n && p->nei[1].n) return; // FIXME: between this and the next line, which is better? + if (p->nei[0].n + p->nei[1].n != 1) return; + dir = p->nei[0].n? 0 : 1; + // initialize the scoring system + for (i = k = 0; i < 4; ++i) + for (j = 0; j < 4; ++j) + mat[k++] = i == j? 5 : -4; + + s = &p->nei[dir]; + for (l = 0; l < s->n; ++l) { // if we use "if (p->nei[0].n + p->nei[1].n != 1)", s->n == 1 + uint64_t v; + kswq_t *qry; + if ((int64_t)s->a[l].x < 0) continue; + v = mag_tid2idd(g->h, s->a[l].x); + q = &g->v.a[v>>1]; + if (q == p || q->nei[v&1].n == 1) continue; + // get the query ready + max_l = (p->len - s->a[l].y) * 2; + seq = malloc(max_l + 1); + if (dir == 0) { // forward strand + for (j = s->a[l].y, k = 0; j < p->len; ++j) + seq[k++] = p->seq[j] - 1; + } else { // reverse + for (j = p->len - s->a[l].y - 1, k = 0; j >= 0; --j) + seq[k++] = 4 - p->seq[j]; + } + l_qry = k; + qry = ksw_qinit(2, l_qry, seq, 4, mat); + //fprintf(stderr, "===> %lld:%lld:%d[%d], %d, %ld <===\n", p->k[0], p->k[1], s->n, l, p->nsr, q->nei[v&1].n); + //for (j = 0; j < k; ++j) fputc("ACGTN"[(int)seq[j]], stderr); fputc('\n', stderr); + + r = &q->nei[v&1]; + for (i = 0; i < r->n; ++i) { + uint64_t w; + kswr_t aln; + if (r->a[i].x == p->k[dir] || (int64_t)r->a[i].x < 0) continue; + w = mag_tid2idd(g->h, r->a[i].x); + // get the target sequence + t = &g->v.a[w>>1]; + if (w&1) { // reverse strand + for (j = t->len - r->a[i].y - 1, k = 0; j >= 0 && k < max_l; --j) + seq[k++] = 4 - t->seq[j]; + } else { + for (j = r->a[i].y, k = 0; j < t->len && k < max_l; ++j) + seq[k++] = t->seq[j] - 1; + } + aln = ksw_align(0, 0, k, seq, 4, mat, 5, 2, 0, &qry); + //for (j = 0; j < k; ++j) fputc("ACGTN"[(int)seq[j]], stderr); fprintf(stderr, "\t%d\t%f\n", aln.score, (l_qry * 5. - aln.score) / (5. + 4.)); + if (aln.score >= l_qry * 5 / 2) { + double r_diff, n_diff; + n_diff = (l_qry * 5. - aln.score) / (5. + 4.); // 5: matching score; -4: mismatchig score + r_diff = n_diff / l_qry; + if (n_diff < MAX_N_DIFF || r_diff < MAX_R_DIFF) break; + } + } + + if (i != r->n) { + // mark delete in p and delete in q + edge_mark_del(s->a[l]); + for (i = 0; i < r->n; ++i) + if (r->a[i].x == p->k[dir]) + edge_mark_del(r->a[i]); + } + free(seq); free(qry); + } + + for (i = 0; i < s->n; ++i) + if (!edge_is_del(s->a[i])) break; + if (i == s->n) mag_v_del(g, p); // p is not connected to any other vertices +} + +void mag_g_pop_open(mag_t *g, int min_elen) +{ + int64_t i; + for (i = 0; i < g->v.n; ++i) + mag_v_pop_open(g, &g->v.a[i], min_elen); + if (fm_verbose >= 3) + fprintf(stderr, "[M:%s] popped open bubbles\n", __func__); + mag_g_merge(g, 0, 0); +} diff --git a/example.c b/example.c new file mode 100644 index 0000000..5cf65dd --- /dev/null +++ b/example.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include "fml.h" + +int main(int argc, char *argv[]) +{ + fml_opt_t opt; + int c, n_seqs, n_utg, gfa_out = 0; + bseq1_t *seqs; + fml_utg_t *utg; + + fml_opt_init(&opt); + while ((c = getopt(argc, argv, "gOAe:l:r:t:c:d:v:")) >= 0) { + if (c == 'e') opt.ec_k = atoi(optarg); + else if (c == 'l') opt.min_asm_ovlp = atoi(optarg); + else if (c == 'r') opt.mag_opt.min_dratio1 = atof(optarg); + else if (c == 'A') opt.mag_opt.flag |= MAG_F_AGGRESSIVE; + else if (c == 'O') opt.mag_opt.flag &= ~MAG_F_POPOPEN; + else if (c == 'd') opt.mag_opt.max_bdiff = atoi(optarg); + else if (c == 't') opt.n_threads = atoi(optarg); + else if (c == 'g') gfa_out = 1; + else if (c == 'v') fm_verbose = atoi(optarg); + else if (c == 'c') { + char *p; + opt.min_cnt = strtol(optarg, &p, 10); + if (*p == ',') opt.max_cnt = strtol(p + 1, &p, 10); + } + } + if (argc == optind) { + fprintf(stderr, "Usage: fml-asm [options] \n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -e INT k-mer length for error correction (0 for auto; -1 to disable) [%d]\n", opt.ec_k); + fprintf(stderr, " -c INT1[,INT2] range of k-mer & read count thresholds for ec and graph cleaning [%d,%d]\n", opt.min_cnt, opt.max_cnt); + fprintf(stderr, " -l INT min overlap length during initial assembly [%d]\n", opt.min_asm_ovlp); + fprintf(stderr, " -r FLOAT drop an overlap if its length is below maxOvlpLen*FLOAT [%g]\n", opt.mag_opt.min_dratio1); + fprintf(stderr, " -t INT number of threads (don't use multi-threading for small data sets) [%d]\n", opt.n_threads); + fprintf(stderr, " -d INT retain a bubble if one side is longer than the other side by >INT-bp [%d]\n", opt.mag_opt.max_bdiff); + fprintf(stderr, " -A discard heterozygotes (apply this to assemble bacterial genomes; override -O)\n"); + fprintf(stderr, " -O don't apply aggressive tip trimming\n"); + fprintf(stderr, " -g output the assembly graph in the GFA format\n"); + return 1; + } + seqs = bseq_read(argv[optind], &n_seqs); + utg = fml_assemble(&opt, n_seqs, seqs, &n_utg); + if (!gfa_out) fml_utg_print(n_utg, utg); + else fml_utg_print_gfa(n_utg, utg); + fml_utg_destroy(n_utg, utg); + return 0; +} diff --git a/fml.h b/fml.h new file mode 100644 index 0000000..fd7d466 --- /dev/null +++ b/fml.h @@ -0,0 +1,193 @@ +#ifndef FML_H +#define FML_H + +#define FML_VERSION "r55" + +#include + +typedef struct { + int32_t l_seq; + char *seq, *qual; // NULL-terminated strings; length expected to match $l_seq +} bseq1_t; + +#define MAG_F_AGGRESSIVE 0x20 // pop variant bubbles (not default) +#define MAG_F_POPOPEN 0x40 // aggressive tip trimming (default) +#define MAG_F_NO_SIMPL 0x80 // skip bubble simplification (default) + +typedef struct { + int flag, min_ovlp, min_elen, min_ensr, min_insr, max_bdist, max_bdiff, max_bvtx, min_merge_len, trim_len, trim_depth; + float min_dratio1, max_bcov, max_bfrac; +} magopt_t; + +typedef struct { + int n_threads; // number of threads; don't use multi-threading for small data sets + int ec_k; // k-mer length for error correction; 0 for auto estimate + int min_cnt, max_cnt; // both occ threshold in ec and tip threshold in cleaning lie in [min_cnt,max_cnt] + int min_asm_ovlp; // min overlap length during assembly + int min_merge_len; // during assembly, don't explicitly merge an overlap if shorter than this value + magopt_t mag_opt; // graph cleaning options +} fml_opt_t; + +struct rld_t; +struct mag_t; + +typedef struct { + uint32_t len:31, from:1; // $from and $to: 0 meaning overlapping 5'-end; 1 overlapping 3'-end + uint32_t id:31, to:1; // $id: unitig number +} fml_ovlp_t; + +typedef struct { + int32_t len; // length of sequence + int32_t nsr; // number of supporting reads + char *seq; // unitig sequence + char *cov; // cov[i]-33 gives per-base coverage at i + int n_ovlp[2]; // number of 5'-end [0] and 3'-end [1] overlaps + fml_ovlp_t *ovlp; // overlaps, of size n_ovlp[0]+n_ovlp[1] +} fml_utg_t; + +extern int fm_verbose; + +#ifdef __cplusplus +extern "C" { +#endif + +/************************ + * High-level functions * + ************************/ + +/** + * Read all sequences from a FASTA/FASTQ file + * + * @param fn filename; NULL or "-" for stdin + * @param n (out) number of sequences read into RAM + * + * @return array of sequences + */ +bseq1_t *bseq_read(const char *fn, int *n); + +/** + * Initialize default parameters + * + * @param opt (out) pointer to parameters + */ +void fml_opt_init(fml_opt_t *opt); + +/** + * Assemble a list of sequences + * + * @param opt parameters + * @param n_seqs number of input sequences + * @param seqs sequences to assemble; FREED on return + * @param n_utg (out) number of unitigs in return + * + * @return array of unitigs + */ +fml_utg_t *fml_assemble(const fml_opt_t *opt, int n_seqs, bseq1_t *seqs, int *n_utg); + +/** + * Free unitigs + * + * @param n_utg number of unitigs + * @param utg array of unitigs + */ +void fml_utg_destroy(int n_utg, fml_utg_t *utg); + +/************************************************ + * Mid-level functions called by fml_assemble() * + ************************************************/ + +/** + * Adjust parameters based on input sequences + * + * @param opt parameters to update IN PLACE + * @param n_seqs number of sequences + * @param seqs array of sequences + */ +void fml_opt_adjust(fml_opt_t *opt, int n_seqs, const bseq1_t *seqs); + +/** + * Error correction + * + * @param opt parameters + * @param n number of sequences + * @param seq array of sequences; corrected IN PLACE + * + * @return k-mer coverage + */ +float fml_correct(const fml_opt_t *opt, int n, bseq1_t *seq); +float fml_fltuniq(const fml_opt_t *opt, int n, bseq1_t *seq); + +/** + * Construct FMD-index + * + * @param opt parameters + * @param n number of sequences + * @param seq array of sequences; FREED on return + * + * @return FMD-index on success; NULL if all input sequences are zero in length + */ +struct rld_t *fml_seq2fmi(const fml_opt_t *opt, int n, bseq1_t *seq); + +/** + * Generate initial overlap graph + * + * @param opt parameters + * @param e FMD-index; FREED on return + * + * @return overlap graph in the "mag" structure + */ +struct mag_t *fml_fmi2mag(const fml_opt_t *opt, struct rld_t *e); + +/** + * Clean a mag graph + * + * @param opt parameters + * @param g overlap graph; modified IN PLACE + */ +void fml_mag_clean(const fml_opt_t *opt, struct mag_t *g); + +/** + * Convert a graph in mag to fml_utg_t + * + * @param g graph in the "mag" structure; FREED on return + * @param n_utg (out) number of unitigs + * + * @return array of unitigs + */ +fml_utg_t *fml_mag2utg(struct mag_t *g, int *n_utg); + +/** + * Output unitig graph in the mag format + * + * @param n_utg number of unitigs + * @param utg array of unitigs + */ +void fml_utg_print(int n_utgs, const fml_utg_t *utg); + +/** + * Output unitig graph in the GFA format + * + * @param n_utg number of unitigs + * @param utg array of unitigs + */ +void fml_utg_print_gfa(int n, const fml_utg_t *utg); + +/** + * Deallocate an FM-index + * + * @param e pointer to the FM-index + */ +void fml_fmi_destroy(struct rld_t *e); + +/** + * Deallocate a mag graph + * + * @param g pointer to the mag graph + */ +void fml_mag_destroy(struct mag_t *g); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/htab.c b/htab.c new file mode 100644 index 0000000..13b4150 --- /dev/null +++ b/htab.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include "htab.h" +#include "khash.h" + +#define _cnt_eq(a, b) ((a)>>14 == (b)>>14) +#define _cnt_hash(a) ((a)>>14) +KHASH_INIT(cnt, uint64_t, char, 0, _cnt_hash, _cnt_eq) +typedef khash_t(cnt) cnthash_t; + +struct bfc_ch_s { + int k; + cnthash_t **h; + // private + int l_pre; +}; + +bfc_ch_t *bfc_ch_init(int k, int l_pre) +{ + bfc_ch_t *ch; + int i; + assert(k <= 63); + if (k * 2 - l_pre > BFC_CH_KEYBITS) + l_pre = k * 2 - BFC_CH_KEYBITS; + if (l_pre > BFC_CH_MAXPRE) l_pre = BFC_CH_MAXPRE; + assert(k - l_pre < BFC_CH_KEYBITS); + ch = calloc(1, sizeof(bfc_ch_t)); + ch->k = k, ch->l_pre = l_pre; + ch->h = calloc(1<l_pre, sizeof(void*)); + for (i = 0; i < 1<l_pre; ++i) + ch->h[i] = kh_init(cnt); + return ch; +} + +void bfc_ch_destroy(bfc_ch_t *ch) +{ + int i; + if (ch == 0) return; + for (i = 0; i < 1<l_pre; ++i) + kh_destroy(cnt, ch->h[i]); + free(ch->h); free(ch); +} + +static inline cnthash_t *get_subhash(const bfc_ch_t *ch, const uint64_t x[2], uint64_t *key) +{ + if (ch->k <= 32) { + int t = ch->k * 2 - ch->l_pre; + uint64_t z = x[0] << ch->k | x[1]; + *key = (z & ((1ULL<h[z>>t]; + } else { + int t = ch->k - ch->l_pre; + int shift = t + ch->k < BFC_CH_KEYBITS? ch->k : BFC_CH_KEYBITS - t; + *key = ((x[0] & ((1ULL<h[x[0]>>t]; + } +} + +int bfc_ch_insert(bfc_ch_t *ch, const uint64_t x[2], int is_high, int forced) +{ + int absent; + uint64_t key; + cnthash_t *h; + khint_t k; + h = get_subhash(ch, x, &key); + if (__sync_lock_test_and_set(&h->lock, 1)) { + if (forced) // then wait until the hash table is unlocked by the thread using it + while (__sync_lock_test_and_set(&h->lock, 1)) + while (h->lock); // lock + else return -1; + } + k = kh_put(cnt, h, key, &absent); + if (absent) { + if (is_high) kh_key(h, k) |= 1<<8; + } else { + if ((kh_key(h, k) & 0xff) != 0xff) ++kh_key(h, k); + if (is_high && (kh_key(h, k) >> 8 & 0x3f) != 0x3f) kh_key(h, k) += 1<<8; + } + __sync_lock_release(&h->lock); // unlock + return 0; +} + +int bfc_ch_get(const bfc_ch_t *ch, const uint64_t x[2]) +{ + uint64_t key; + cnthash_t *h; + khint_t itr; + h = get_subhash(ch, x, &key); + itr = kh_get(cnt, h, key); + return itr == kh_end(h)? -1 : kh_key(h, itr) & 0x3fff; +} + +int bfc_ch_kmer_occ(const bfc_ch_t *ch, const bfc_kmer_t *z) +{ + uint64_t x[2]; + bfc_kmer_hash(ch->k, z->x, x); + return bfc_ch_get(ch, x); +} + +uint64_t bfc_ch_count(const bfc_ch_t *ch) +{ + int i; + uint64_t cnt = 0; + for (i = 0; i < 1<l_pre; ++i) + cnt += kh_size(ch->h[i]); + return cnt; +} + +int bfc_ch_hist(const bfc_ch_t *ch, uint64_t cnt[256], uint64_t high[64]) +{ + int i, max_i = -1; + uint64_t max; + memset(cnt, 0, 256 * 8); + memset(high, 0, 64 * 8); + for (i = 0; i < 1<l_pre; ++i) { + khint_t k; + cnthash_t *h = ch->h[i]; + for (k = 0; k != kh_end(h); ++k) + if (kh_exist(h, k)) + ++cnt[kh_key(h, k) & 0xff], ++high[kh_key(h, k)>>8 & 0x3f]; + } + for (i = 3, max = 0; i < 256; ++i) + if (cnt[i] > max) + max = cnt[i], max_i = i; + return max_i; +} + +int bfc_ch_get_k(const bfc_ch_t *ch) +{ + return ch->k; +} diff --git a/htab.h b/htab.h new file mode 100644 index 0000000..118244c --- /dev/null +++ b/htab.h @@ -0,0 +1,23 @@ +#ifndef BFC_HTAB_H +#define BFC_HTAB_H + +#include +#include "kmer.h" + +#define BFC_CH_KEYBITS 50 +#define BFC_CH_MAXPRE 20 + +struct bfc_ch_s; +typedef struct bfc_ch_s bfc_ch_t; + +bfc_ch_t *bfc_ch_init(int k, int l_pre); +void bfc_ch_destroy(bfc_ch_t *ch); +int bfc_ch_insert(bfc_ch_t *ch, const uint64_t x[2], int is_high, int forced); +int bfc_ch_get(const bfc_ch_t *ch, const uint64_t x[2]); +uint64_t bfc_ch_count(const bfc_ch_t *ch); +int bfc_ch_hist(const bfc_ch_t *ch, uint64_t cnt[256], uint64_t high[64]); +int bfc_ch_get_k(const bfc_ch_t *ch); + +int bfc_ch_kmer_occ(const bfc_ch_t *ch, const bfc_kmer_t *z); + +#endif diff --git a/internal.h b/internal.h new file mode 100644 index 0000000..37e4d96 --- /dev/null +++ b/internal.h @@ -0,0 +1,21 @@ +#ifndef FML_INTERNAL_H +#define FML_INTERNAL_H + +#include "fml.h" + +extern unsigned char seq_nt6_table[256]; + +#ifdef __cplusplus +extern "C" { +#endif + +void kt_for(int n_threads, void (*func)(void*,long,int), void *data, long n); +void seq_reverse(int l, unsigned char *s); +void seq_revcomp6(int l, unsigned char *s); +struct bfc_ch_s *fml_count(int n, const bseq1_t *seq, int k, int q, int l_pre, int n_threads); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/khash.h b/khash.h new file mode 100644 index 0000000..870ae5d --- /dev/null +++ b/khash.h @@ -0,0 +1,614 @@ +/* The MIT License + + Copyright (c) 2008, 2009, 2011 by Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + An example: + +#include "khash.h" +KHASH_MAP_INIT_INT(32, char) +int main() { + int ret, is_missing; + khiter_t k; + khash_t(32) *h = kh_init(32); + k = kh_put(32, h, 5, &ret); + kh_value(h, k) = 10; + k = kh_get(32, h, 10); + is_missing = (k == kh_end(h)); + k = kh_get(32, h, 5); + kh_del(32, h, k); + for (k = kh_begin(h); k != kh_end(h); ++k) + if (kh_exist(h, k)) kh_value(h, k) = 1; + kh_destroy(32, h); + return 0; +} +*/ + +/* + 2013-05-02 (0.2.8): + + * Use quadratic probing. When the capacity is power of 2, stepping function + i*(i+1)/2 guarantees to traverse each bucket. It is better than double + hashing on cache performance and is more robust than linear probing. + + In theory, double hashing should be more robust than quadratic probing. + However, my implementation is probably not for large hash tables, because + the second hash function is closely tied to the first hash function, + which reduce the effectiveness of double hashing. + + Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php + + 2011-12-29 (0.2.7): + + * Minor code clean up; no actual effect. + + 2011-09-16 (0.2.6): + + * The capacity is a power of 2. This seems to dramatically improve the + speed for simple keys. Thank Zilong Tan for the suggestion. Reference: + + - http://code.google.com/p/ulib/ + - http://nothings.org/computer/judy/ + + * Allow to optionally use linear probing which usually has better + performance for random input. Double hashing is still the default as it + is more robust to certain non-random input. + + * Added Wang's integer hash function (not used by default). This hash + function is more robust to certain non-random input. + + 2011-02-14 (0.2.5): + + * Allow to declare global functions. + + 2009-09-26 (0.2.4): + + * Improve portability + + 2008-09-19 (0.2.3): + + * Corrected the example + * Improved interfaces + + 2008-09-11 (0.2.2): + + * Improved speed a little in kh_put() + + 2008-09-10 (0.2.1): + + * Added kh_clear() + * Fixed a compiling error + + 2008-09-02 (0.2.0): + + * Changed to token concatenation which increases flexibility. + + 2008-08-31 (0.1.2): + + * Fixed a bug in kh_get(), which has not been tested previously. + + 2008-08-31 (0.1.1): + + * Added destructor +*/ + + +#ifndef __AC_KHASH_H +#define __AC_KHASH_H + +/*! + @header + + Generic hash table library. + */ + +#define AC_VERSION_KHASH_H "0.2.8" + +#include +#include +#include + +/* compiler specific configuration */ + +#if UINT_MAX == 0xffffffffu +typedef unsigned int khint32_t; +#elif ULONG_MAX == 0xffffffffu +typedef unsigned long khint32_t; +#endif + +#if ULONG_MAX == ULLONG_MAX +typedef unsigned long khint64_t; +#else +typedef unsigned long long khint64_t; +#endif + +#ifdef _MSC_VER +#define kh_inline __inline +#else +#define kh_inline inline +#endif + +typedef khint32_t khint_t; +typedef khint_t khiter_t; + +#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) +#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) +#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) +#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) +#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) +#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) +#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) + +#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) + +#ifndef kroundup32 +#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#endif + +#ifndef kcalloc +#define kcalloc(N,Z) calloc(N,Z) +#endif +#ifndef kmalloc +#define kmalloc(Z) malloc(Z) +#endif +#ifndef krealloc +#define krealloc(P,Z) realloc(P,Z) +#endif +#ifndef kfree +#define kfree(P) free(P) +#endif + +#define __KHASH_TYPE(name, khkey_t, khval_t) \ + typedef struct kh_##name##_s { \ + khint_t n_buckets, size, n_occupied; \ + volatile int lock; \ + khint32_t *flags; \ + khkey_t *keys; \ + khval_t *vals; \ + } kh_##name##_t; + +#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ + extern kh_##name##_t *kh_init_##name(void); \ + extern void kh_destroy_##name(kh_##name##_t *h); \ + extern void kh_clear_##name(kh_##name##_t *h); \ + extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ + extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ + extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ + extern void kh_del_##name(kh_##name##_t *h, khint_t x); + +#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + SCOPE kh_##name##_t *kh_init_##name(void) { \ + return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \ + } \ + SCOPE void kh_destroy_##name(kh_##name##_t *h) \ + { \ + if (h) { \ + kfree((void *)h->keys); kfree(h->flags); \ + kfree((void *)h->vals); \ + kfree(h); \ + } \ + } \ + SCOPE void kh_clear_##name(kh_##name##_t *h) \ + { \ + if (h && h->flags) { \ + memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \ + h->size = h->n_occupied = 0; \ + } \ + } \ + SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ + { \ + if (h->n_buckets) { \ + khint_t k, i, last, mask, step = 0; \ + mask = h->n_buckets - 1; \ + k = __hash_func(key); i = k & mask; \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + i = (i + (++step)) & mask; \ + if (i == last) return h->n_buckets; \ + } \ + return __ac_iseither(h->flags, i)? h->n_buckets : i; \ + } else return 0; \ + } \ + SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ + { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \ + khint32_t *new_flags = 0; \ + khint_t j = 1; \ + { \ + kroundup32(new_n_buckets); \ + if (new_n_buckets < 4) new_n_buckets = 4; \ + if (h->size >= (new_n_buckets>>1) + (new_n_buckets>>2)) j = 0; /* requested size is too small */ \ + else { /* hash table size to be changed (shrink or expand); rehash */ \ + new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (!new_flags) return -1; \ + memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (h->n_buckets < new_n_buckets) { /* expand */ \ + khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (!new_keys) return -1; \ + h->keys = new_keys; \ + if (kh_is_map) { \ + khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + if (!new_vals) return -1; \ + h->vals = new_vals; \ + } \ + } /* otherwise shrink */ \ + } \ + } \ + if (j) { /* rehashing is needed */ \ + for (j = 0; j != h->n_buckets; ++j) { \ + if (__ac_iseither(h->flags, j) == 0) { \ + khkey_t key = h->keys[j]; \ + khval_t val; \ + khint_t new_mask; \ + new_mask = new_n_buckets - 1; \ + if (kh_is_map) val = h->vals[j]; \ + __ac_set_isdel_true(h->flags, j); \ + while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \ + khint_t k, i, step = 0; \ + k = __hash_func(key); \ + i = k & new_mask; \ + while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \ + __ac_set_isempty_false(new_flags, i); \ + if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \ + { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ + if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ + __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \ + } else { /* write the element and jump out of the loop */ \ + h->keys[i] = key; \ + if (kh_is_map) h->vals[i] = val; \ + break; \ + } \ + } \ + } \ + } \ + if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ + h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + } \ + kfree(h->flags); /* free the working space */ \ + h->flags = new_flags; \ + h->n_buckets = new_n_buckets; \ + h->n_occupied = h->size; \ + } \ + return 0; \ + } \ + SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ + { \ + khint_t x; \ + if (h->n_occupied >= (h->n_buckets>>2) + (h->n_buckets>>1)) { /* update the hash table */ \ + if (h->n_buckets > (h->size<<1)) { \ + if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \ + *ret = -1; return h->n_buckets; \ + } \ + } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \ + *ret = -1; return h->n_buckets; \ + } \ + } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \ + { \ + khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \ + x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \ + if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \ + else { \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + if (__ac_isdel(h->flags, i)) site = i; \ + i = (i + (++step)) & mask; \ + if (i == last) { x = site; break; } \ + } \ + if (x == h->n_buckets) { \ + if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ + else x = i; \ + } \ + } \ + } \ + if (__ac_isempty(h->flags, x)) { /* not present at all */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; ++h->n_occupied; \ + *ret = 1; \ + } else if (__ac_isdel(h->flags, x)) { /* deleted */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; \ + *ret = 2; \ + } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ + return x; \ + } \ + SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ + { \ + if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ + __ac_set_isdel_true(h->flags, x); \ + --h->size; \ + } \ + } + +#define KHASH_DECLARE(name, khkey_t, khval_t) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_PROTOTYPES(name, khkey_t, khval_t) + +#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +/* --- BEGIN OF HASH FUNCTIONS --- */ + +/*! @function + @abstract Integer hash function + @param key The integer [khint32_t] + @return The hash value [khint_t] + */ +#define kh_int_hash_func(key) (khint32_t)(key) +/*! @function + @abstract Integer comparison function + */ +#define kh_int_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract 64-bit integer hash function + @param key The integer [khint64_t] + @return The hash value [khint_t] + */ +#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) +/*! @function + @abstract 64-bit integer comparison function + */ +#define kh_int64_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract const char* hash function + @param s Pointer to a null terminated string + @return The hash value + */ +static kh_inline khint_t __ac_X31_hash_string(const char *s) +{ + khint_t h = (khint_t)*s; + if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; + return h; +} +/*! @function + @abstract Another interface to const char* hash function + @param key Pointer to a null terminated string [const char*] + @return The hash value [khint_t] + */ +#define kh_str_hash_func(key) __ac_X31_hash_string(key) +/*! @function + @abstract Const char* comparison function + */ +#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) + +static kh_inline khint_t __ac_Wang_hash(khint_t key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} +#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key) + +/* --- END OF HASH FUNCTIONS --- */ + +/* Other convenient macros... */ + +/*! + @abstract Type of the hash table. + @param name Name of the hash table [symbol] + */ +#define khash_t(name) kh_##name##_t + +/*! @function + @abstract Initiate a hash table. + @param name Name of the hash table [symbol] + @return Pointer to the hash table [khash_t(name)*] + */ +#define kh_init(name) kh_init_##name() + +/*! @function + @abstract Destroy a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_destroy(name, h) kh_destroy_##name(h) + +/*! @function + @abstract Reset a hash table without deallocating memory. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_clear(name, h) kh_clear_##name(h) + +/*! @function + @abstract Resize a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param s New size [khint_t] + */ +#define kh_resize(name, h, s) kh_resize_##name(h, s) + +/*! @function + @abstract Insert a key to the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @param r Extra return code: 0 if the key is present in the hash table; + 1 if the bucket is empty (never used); 2 if the element in + the bucket has been deleted [int*] + @return Iterator to the inserted element [khint_t] + */ +#define kh_put(name, h, k, r) kh_put_##name(h, k, r) + +/*! @function + @abstract Retrieve a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t] + */ +#define kh_get(name, h, k) kh_get_##name(h, k) + +/*! @function + @abstract Remove a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Iterator to the element to be deleted [khint_t] + */ +#define kh_del(name, h, k) kh_del_##name(h, k) + +/*! @function + @abstract Test whether a bucket contains data. + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return 1 if containing data; 0 otherwise [int] + */ +#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) + +/*! @function + @abstract Get key given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Key [type of keys] + */ +#define kh_key(h, x) ((h)->keys[x]) + +/*! @function + @abstract Get value given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Value [type of values] + @discussion For hash sets, calling this results in segfault. + */ +#define kh_val(h, x) ((h)->vals[x]) + +/*! @function + @abstract Alias of kh_val() + */ +#define kh_value(h, x) ((h)->vals[x]) + +/*! @function + @abstract Get the start iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The start iterator [khint_t] + */ +#define kh_begin(h) (khint_t)(0) + +/*! @function + @abstract Get the end iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The end iterator [khint_t] + */ +#define kh_end(h) ((h)->n_buckets) + +/*! @function + @abstract Get the number of elements in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of elements in the hash table [khint_t] + */ +#define kh_size(h) ((h)->size) + +/*! @function + @abstract Get the number of buckets in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of buckets in the hash table [khint_t] + */ +#define kh_n_buckets(h) ((h)->n_buckets) + +/*! @function + @abstract Iterate over the entries in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (kvar) = kh_key(h,__i); \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the values in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach_value(h, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/* More conenient interfaces */ + +/*! @function + @abstract Instantiate a hash set containing integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT(name) \ + KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT(name, khval_t) \ + KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT64(name) \ + KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT64(name, khval_t) \ + KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +typedef const char *kh_cstr_t; +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_STR(name) \ + KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_STR(name, khval_t) \ + KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) + +#endif /* __AC_KHASH_H */ diff --git a/kmer.h b/kmer.h new file mode 100644 index 0000000..b53b7d2 --- /dev/null +++ b/kmer.h @@ -0,0 +1,106 @@ +#ifndef BFC_KMER_H +#define BFC_KMER_H + +#include + +typedef struct { + uint64_t x[4]; +} bfc_kmer_t; + +static inline void bfc_kmer_append(int k, uint64_t x[4], int c) +{ // IMPORTANT: 0 <= c < 4 + uint64_t mask = (1ULL<>1)) & mask; + x[2] = x[2]>>1 | (1ULL^(c&1))<<(k-1); + x[3] = x[3]>>1 | (1ULL^c>>1) <<(k-1); +} + +static inline void bfc_kmer_change(int k, uint64_t x[4], int d, int c) // d-bp from the 3'-end of k-mer; 0<=d>1)<>1)<<(k-1-d) | (x[3]&t); +} + +// Thomas Wang's integer hash functions. See for a snapshot. +static inline uint64_t bfc_hash_64(uint64_t key, uint64_t mask) +{ + key = (~key + (key << 21)) & mask; // key = (key << 21) - key - 1; + key = key ^ key >> 24; + key = ((key + (key << 3)) + (key << 8)) & mask; // key * 265 + key = key ^ key >> 14; + key = ((key + (key << 2)) + (key << 4)) & mask; // key * 21 + key = key ^ key >> 28; + key = (key + (key << 31)) & mask; + return key; +} + +static inline uint64_t bfc_hash_64_inv(uint64_t key, uint64_t mask) +{ + uint64_t tmp; + + // Invert key = key + (key << 31) + tmp = (key - (key << 31)); + key = (key - (tmp << 31)) & mask; + + // Invert key = key ^ (key >> 28) + tmp = key ^ key >> 28; + key = key ^ tmp >> 28; + + // Invert key *= 21 + key = (key * 14933078535860113213ull) & mask; + + // Invert key = key ^ (key >> 14) + tmp = key ^ key >> 14; + tmp = key ^ tmp >> 14; + tmp = key ^ tmp >> 14; + key = key ^ tmp >> 14; + + // Invert key *= 265 + key = (key * 15244667743933553977ull) & mask; + + // Invert key = key ^ (key >> 24) + tmp = key ^ key >> 24; + key = key ^ tmp >> 24; + + // Invert key = (~key) + (key << 21) + tmp = ~key; + tmp = ~(key - (tmp << 21)); + tmp = ~(key - (tmp << 21)); + key = ~(key - (tmp << 21)) & mask; + + return key; +} + +static inline uint64_t bfc_kmer_hash(int k, const uint64_t x[4], uint64_t h[2]) +{ + int t = k>>1, u = ((x[1]>>t&1) > (x[3]>>t&1)); // the middle base is always different + uint64_t mask = (1ULL<>l&1)<<1 | (y[0]>>l&1)]; + buf[k] = 0; + return buf; +} + +#endif diff --git a/kseq.h b/kseq.h new file mode 100644 index 0000000..84c1fa3 --- /dev/null +++ b/kseq.h @@ -0,0 +1,248 @@ +/* The MIT License + + Copyright (c) 2008, 2009, 2011 Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* Last Modified: 05MAR2012 */ + +#ifndef AC_KSEQ_H +#define AC_KSEQ_H + +#include +#include +#include + +#define KS_SEP_SPACE 0 // isspace(): \t, \n, \v, \f, \r +#define KS_SEP_TAB 1 // isspace() && !' ' +#define KS_SEP_LINE 2 // line separator: "\n" (Unix) or "\r\n" (Windows) +#define KS_SEP_MAX 2 + +#define __KS_TYPE(type_t) \ + typedef struct __kstream_t { \ + int begin, end; \ + int is_eof:2, bufsize:30; \ + type_t f; \ + unsigned char *buf; \ + } kstream_t; + +#define ks_eof(ks) ((ks)->is_eof && (ks)->begin >= (ks)->end) +#define ks_rewind(ks) ((ks)->is_eof = (ks)->begin = (ks)->end = 0) + +#define __KS_BASIC(SCOPE, type_t, __bufsize) \ + SCOPE kstream_t *ks_init(type_t f) \ + { \ + kstream_t *ks = (kstream_t*)calloc(1, sizeof(kstream_t)); \ + ks->f = f; ks->bufsize = __bufsize; \ + ks->buf = (unsigned char*)malloc(__bufsize); \ + return ks; \ + } \ + SCOPE void ks_destroy(kstream_t *ks) \ + { \ + if (!ks) return; \ + free(ks->buf); \ + free(ks); \ + } + +#define __KS_INLINED(__read) \ + static inline int ks_getc(kstream_t *ks) \ + { \ + if (ks->is_eof && ks->begin >= ks->end) return -1; \ + if (ks->begin >= ks->end) { \ + ks->begin = 0; \ + ks->end = __read(ks->f, ks->buf, ks->bufsize); \ + if (ks->end < ks->bufsize) ks->is_eof = 1; \ + if (ks->end == 0) return -1; \ + } \ + return (int)ks->buf[ks->begin++]; \ + } \ + static inline int ks_getuntil(kstream_t *ks, int delimiter, kstring_t *str, int *dret) \ + { return ks_getuntil2(ks, delimiter, str, dret, 0); } + +#ifndef KSTRING_T +#define KSTRING_T kstring_t +typedef struct __kstring_t { + size_t l, m; + char *s; +} kstring_t; +#endif + +#ifndef kroundup32 +#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#endif + +#define __KS_GETUNTIL(SCOPE, __read) \ + SCOPE int ks_getuntil2(kstream_t *ks, int delimiter, kstring_t *str, int *dret, int append) \ + { \ + if (dret) *dret = 0; \ + str->l = append? str->l : 0; \ + if (ks->begin >= ks->end && ks->is_eof) return -1; \ + for (;;) { \ + int i; \ + if (ks->begin >= ks->end) { \ + if (!ks->is_eof) { \ + ks->begin = 0; \ + ks->end = __read(ks->f, ks->buf, ks->bufsize); \ + if (ks->end < ks->bufsize) ks->is_eof = 1; \ + if (ks->end == 0) break; \ + } else break; \ + } \ + if (delimiter == KS_SEP_LINE) { \ + for (i = ks->begin; i < ks->end; ++i) \ + if (ks->buf[i] == '\n') break; \ + } else if (delimiter > KS_SEP_MAX) { \ + for (i = ks->begin; i < ks->end; ++i) \ + if (ks->buf[i] == delimiter) break; \ + } else if (delimiter == KS_SEP_SPACE) { \ + for (i = ks->begin; i < ks->end; ++i) \ + if (isspace(ks->buf[i])) break; \ + } else if (delimiter == KS_SEP_TAB) { \ + for (i = ks->begin; i < ks->end; ++i) \ + if (isspace(ks->buf[i]) && ks->buf[i] != ' ') break; \ + } else i = 0; /* never come to here! */ \ + if (str->m - str->l < (size_t)(i - ks->begin + 1)) { \ + str->m = str->l + (i - ks->begin) + 1; \ + kroundup32(str->m); \ + str->s = (char*)realloc(str->s, str->m); \ + } \ + memcpy(str->s + str->l, ks->buf + ks->begin, i - ks->begin); \ + str->l = str->l + (i - ks->begin); \ + ks->begin = i + 1; \ + if (i < ks->end) { \ + if (dret) *dret = ks->buf[i]; \ + break; \ + } \ + } \ + if (str->s == 0) { \ + str->m = 1; \ + str->s = (char*)calloc(1, 1); \ + } else if (delimiter == KS_SEP_LINE && str->l > 1 && str->s[str->l-1] == '\r') --str->l; \ + str->s[str->l] = '\0'; \ + return str->l; \ + } + +#define KSTREAM_INIT2(SCOPE, type_t, __read, __bufsize) \ + __KS_TYPE(type_t) \ + __KS_BASIC(SCOPE, type_t, __bufsize) \ + __KS_GETUNTIL(SCOPE, __read) \ + __KS_INLINED(__read) + +#define KSTREAM_INIT(type_t, __read, __bufsize) KSTREAM_INIT2(static, type_t, __read, __bufsize) + +#define KSTREAM_DECLARE(type_t, __read) \ + __KS_TYPE(type_t) \ + extern int ks_getuntil2(kstream_t *ks, int delimiter, kstring_t *str, int *dret, int append); \ + extern kstream_t *ks_init(type_t f); \ + extern void ks_destroy(kstream_t *ks); \ + __KS_INLINED(__read) + +/****************** + * FASTA/Q parser * + ******************/ + +#define kseq_rewind(ks) ((ks)->last_char = (ks)->f->is_eof = (ks)->f->begin = (ks)->f->end = 0) + +#define __KSEQ_BASIC(SCOPE, type_t) \ + SCOPE kseq_t *kseq_init(type_t fd) \ + { \ + kseq_t *s = (kseq_t*)calloc(1, sizeof(kseq_t)); \ + s->f = ks_init(fd); \ + return s; \ + } \ + SCOPE void kseq_destroy(kseq_t *ks) \ + { \ + if (!ks) return; \ + free(ks->name.s); free(ks->comment.s); free(ks->seq.s); free(ks->qual.s); \ + ks_destroy(ks->f); \ + free(ks); \ + } + +/* Return value: + >=0 length of the sequence (normal) + -1 end-of-file + -2 truncated quality string + */ +#define __KSEQ_READ(SCOPE) \ + SCOPE int kseq_read(kseq_t *seq) \ + { \ + int c; \ + kstream_t *ks = seq->f; \ + if (seq->last_char == 0) { /* then jump to the next header line */ \ + while ((c = ks_getc(ks)) != -1 && c != '>' && c != '@'); \ + if (c == -1) return -1; /* end of file */ \ + seq->last_char = c; \ + } /* else: the first header char has been read in the previous call */ \ + seq->comment.l = seq->seq.l = seq->qual.l = 0; /* reset all members */ \ + if (ks_getuntil(ks, 0, &seq->name, &c) < 0) return -1; /* normal exit: EOF */ \ + if (c != '\n') ks_getuntil(ks, KS_SEP_LINE, &seq->comment, 0); /* read FASTA/Q comment */ \ + if (seq->seq.s == 0) { /* we can do this in the loop below, but that is slower */ \ + seq->seq.m = 256; \ + seq->seq.s = (char*)malloc(seq->seq.m); \ + } \ + while ((c = ks_getc(ks)) != -1 && c != '>' && c != '+' && c != '@') { \ + if (c == '\n') continue; /* skip empty lines */ \ + seq->seq.s[seq->seq.l++] = c; /* this is safe: we always have enough space for 1 char */ \ + ks_getuntil2(ks, KS_SEP_LINE, &seq->seq, 0, 1); /* read the rest of the line */ \ + } \ + if (c == '>' || c == '@') seq->last_char = c; /* the first header char has been read */ \ + if (seq->seq.l + 1 >= seq->seq.m) { /* seq->seq.s[seq->seq.l] below may be out of boundary */ \ + seq->seq.m = seq->seq.l + 2; \ + kroundup32(seq->seq.m); /* rounded to the next closest 2^k */ \ + seq->seq.s = (char*)realloc(seq->seq.s, seq->seq.m); \ + } \ + seq->seq.s[seq->seq.l] = 0; /* null terminated string */ \ + if (c != '+') return seq->seq.l; /* FASTA */ \ + if (seq->qual.m < seq->seq.m) { /* allocate memory for qual in case insufficient */ \ + seq->qual.m = seq->seq.m; \ + seq->qual.s = (char*)realloc(seq->qual.s, seq->qual.m); \ + } \ + while ((c = ks_getc(ks)) != -1 && c != '\n'); /* skip the rest of '+' line */ \ + if (c == -1) return -2; /* error: no quality string */ \ + while (ks_getuntil2(ks, KS_SEP_LINE, &seq->qual, 0, 1) >= 0 && seq->qual.l < seq->seq.l); \ + seq->last_char = 0; /* we have not come to the next header line */ \ + if (seq->seq.l != seq->qual.l) return -2; /* error: qual string is of a different length */ \ + return seq->seq.l; \ + } + +#define __KSEQ_TYPE(type_t) \ + typedef struct { \ + kstring_t name, comment, seq, qual; \ + int last_char; \ + kstream_t *f; \ + } kseq_t; + +#define KSEQ_INIT2(SCOPE, type_t, __read) \ + KSTREAM_INIT2(SCOPE, type_t, __read, 16384) \ + __KSEQ_TYPE(type_t) \ + __KSEQ_BASIC(SCOPE, type_t) \ + __KSEQ_READ(SCOPE) + +#define KSEQ_INIT(type_t, __read) KSEQ_INIT2(static, type_t, __read) + +#define KSEQ_DECLARE(type_t) \ + __KS_TYPE(type_t) \ + __KSEQ_TYPE(type_t) \ + extern kseq_t *kseq_init(type_t fd); \ + void kseq_destroy(kseq_t *ks); \ + int kseq_read(kseq_t *seq); + +#endif diff --git a/ksort.h b/ksort.h new file mode 100644 index 0000000..e659b22 --- /dev/null +++ b/ksort.h @@ -0,0 +1,309 @@ +/* The MIT License + + Copyright (c) 2008, 2011 Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + 2011-04-10 (0.1.6): + + * Added sample + + 2011-03 (0.1.5): + + * Added shuffle/permutation + + 2008-11-16 (0.1.4): + + * Fixed a bug in introsort() that happens in rare cases. + + 2008-11-05 (0.1.3): + + * Fixed a bug in introsort() for complex comparisons. + + * Fixed a bug in mergesort(). The previous version is not stable. + + 2008-09-15 (0.1.2): + + * Accelerated introsort. On my Mac (not on another Linux machine), + my implementation is as fast as std::sort on random input. + + * Added combsort and in introsort, switch to combsort if the + recursion is too deep. + + 2008-09-13 (0.1.1): + + * Added k-small algorithm + + 2008-09-05 (0.1.0): + + * Initial version + +*/ + +#ifndef AC_KSORT_H +#define AC_KSORT_H + +#include +#include + +typedef struct { + void *left, *right; + int depth; +} ks_isort_stack_t; + +#define KSORT_SWAP(type_t, a, b) { register type_t t=(a); (a)=(b); (b)=t; } + +#define KSORT_INIT(name, type_t, __sort_lt) \ + void ks_mergesort_##name(size_t n, type_t array[], type_t temp[]) \ + { \ + type_t *a2[2], *a, *b; \ + int curr, shift; \ + \ + a2[0] = array; \ + a2[1] = temp? temp : (type_t*)malloc(sizeof(type_t) * n); \ + for (curr = 0, shift = 0; (1ul<> 1; \ + if (__sort_lt(tmp, l[i])) break; \ + l[k] = l[i]; k = i; \ + } \ + l[k] = tmp; \ + } \ + void ks_heapmake_##name(size_t lsize, type_t l[]) \ + { \ + size_t i; \ + for (i = (lsize >> 1) - 1; i != (size_t)(-1); --i) \ + ks_heapdown_##name(i, lsize, l); \ + } \ + void ks_heapsort_##name(size_t lsize, type_t l[]) \ + { \ + size_t i; \ + for (i = lsize - 1; i > 0; --i) { \ + type_t tmp; \ + tmp = *l; *l = l[i]; l[i] = tmp; ks_heapdown_##name(0, i, l); \ + } \ + } \ + static inline void __ks_insertsort_##name(type_t *s, type_t *t) \ + { \ + type_t *i, *j, swap_tmp; \ + for (i = s + 1; i < t; ++i) \ + for (j = i; j > s && __sort_lt(*j, *(j-1)); --j) { \ + swap_tmp = *j; *j = *(j-1); *(j-1) = swap_tmp; \ + } \ + } \ + void ks_combsort_##name(size_t n, type_t a[]) \ + { \ + const double shrink_factor = 1.2473309501039786540366528676643; \ + int do_swap; \ + size_t gap = n; \ + type_t tmp, *i, *j; \ + do { \ + if (gap > 2) { \ + gap = (size_t)(gap / shrink_factor); \ + if (gap == 9 || gap == 10) gap = 11; \ + } \ + do_swap = 0; \ + for (i = a; i < a + n - gap; ++i) { \ + j = i + gap; \ + if (__sort_lt(*j, *i)) { \ + tmp = *i; *i = *j; *j = tmp; \ + do_swap = 1; \ + } \ + } \ + } while (do_swap || gap > 2); \ + if (gap != 1) __ks_insertsort_##name(a, a + n); \ + } \ + void ks_introsort_##name(size_t n, type_t a[]) \ + { \ + int d; \ + ks_isort_stack_t *top, *stack; \ + type_t rp, swap_tmp; \ + type_t *s, *t, *i, *j, *k; \ + \ + if (n < 1) return; \ + else if (n == 2) { \ + if (__sort_lt(a[1], a[0])) { swap_tmp = a[0]; a[0] = a[1]; a[1] = swap_tmp; } \ + return; \ + } \ + for (d = 2; 1ul<>1) + 1; \ + if (__sort_lt(*k, *i)) { \ + if (__sort_lt(*k, *j)) k = j; \ + } else k = __sort_lt(*j, *i)? i : j; \ + rp = *k; \ + if (k != t) { swap_tmp = *k; *k = *t; *t = swap_tmp; } \ + for (;;) { \ + do ++i; while (__sort_lt(*i, rp)); \ + do --j; while (i <= j && __sort_lt(rp, *j)); \ + if (j <= i) break; \ + swap_tmp = *i; *i = *j; *j = swap_tmp; \ + } \ + swap_tmp = *i; *i = *t; *t = swap_tmp; \ + if (i-s > t-i) { \ + if (i-s > 16) { top->left = s; top->right = i-1; top->depth = d; ++top; } \ + s = t-i > 16? i+1 : t; \ + } else { \ + if (t-i > 16) { top->left = i+1; top->right = t; top->depth = d; ++top; } \ + t = i-s > 16? i-1 : s; \ + } \ + } else { \ + if (top == stack) { \ + free(stack); \ + __ks_insertsort_##name(a, a+n); \ + return; \ + } else { --top; s = (type_t*)top->left; t = (type_t*)top->right; d = top->depth; } \ + } \ + } \ + } \ + /* This function is adapted from: http://ndevilla.free.fr/median/ */ \ + /* 0 <= kk < n */ \ + type_t ks_ksmall_##name(size_t n, type_t arr[], size_t kk) \ + { \ + type_t *low, *high, *k, *ll, *hh, *mid; \ + low = arr; high = arr + n - 1; k = arr + kk; \ + for (;;) { \ + if (high <= low) return *k; \ + if (high == low + 1) { \ + if (__sort_lt(*high, *low)) KSORT_SWAP(type_t, *low, *high); \ + return *k; \ + } \ + mid = low + (high - low) / 2; \ + if (__sort_lt(*high, *mid)) KSORT_SWAP(type_t, *mid, *high); \ + if (__sort_lt(*high, *low)) KSORT_SWAP(type_t, *low, *high); \ + if (__sort_lt(*low, *mid)) KSORT_SWAP(type_t, *mid, *low); \ + KSORT_SWAP(type_t, *mid, *(low+1)); \ + ll = low + 1; hh = high; \ + for (;;) { \ + do ++ll; while (__sort_lt(*ll, *low)); \ + do --hh; while (__sort_lt(*low, *hh)); \ + if (hh < ll) break; \ + KSORT_SWAP(type_t, *ll, *hh); \ + } \ + KSORT_SWAP(type_t, *low, *hh); \ + if (hh <= k) low = ll; \ + if (hh >= k) high = hh - 1; \ + } \ + } \ + void ks_shuffle_##name(size_t n, type_t a[]) \ + { \ + int i, j; \ + for (i = n; i > 1; --i) { \ + type_t tmp; \ + j = (int)(drand48() * i); \ + tmp = a[j]; a[j] = a[i-1]; a[i-1] = tmp; \ + } \ + } \ + void ks_sample_##name(size_t n, size_t r, type_t a[]) /* FIXME: NOT TESTED!!! */ \ + { /* reference: http://code.activestate.com/recipes/272884/ */ \ + int i, k, pop = n; \ + for (i = (int)r, k = 0; i >= 0; --i) { \ + double z = 1., x = drand48(); \ + type_t tmp; \ + while (x < z) z -= z * i / (pop--); \ + if (k != n - pop - 1) tmp = a[k], a[k] = a[n-pop-1], a[n-pop-1] = tmp; \ + ++k; \ + } \ + } + +#define ks_mergesort(name, n, a, t) ks_mergesort_##name(n, a, t) +#define ks_introsort(name, n, a) ks_introsort_##name(n, a) +#define ks_combsort(name, n, a) ks_combsort_##name(n, a) +#define ks_heapsort(name, n, a) ks_heapsort_##name(n, a) +#define ks_heapmake(name, n, a) ks_heapmake_##name(n, a) +#define ks_heapadjust(name, i, n, a) ks_heapadjust_##name(i, n, a) +#define ks_ksmall(name, n, a, k) ks_ksmall_##name(n, a, k) +#define ks_shuffle(name, n, a) ks_shuffle_##name(n, a) + +#define ks_lt_generic(a, b) ((a) < (b)) +#define ks_lt_str(a, b) (strcmp((a), (b)) < 0) + +typedef const char *ksstr_t; + +#define KSORT_INIT_GENERIC(type_t) KSORT_INIT(type_t, type_t, ks_lt_generic) +#define KSORT_INIT_STR KSORT_INIT(str, ksstr_t, ks_lt_str) + +#endif diff --git a/kstring.h b/kstring.h new file mode 100644 index 0000000..b179a8c --- /dev/null +++ b/kstring.h @@ -0,0 +1,169 @@ +/* The MIT License + + Copyright (c) by Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef KSTRING_H +#define KSTRING_H + +#include +#include +#include + +#ifndef kroundup32 +#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#endif + +#ifndef KSTRING_T +#define KSTRING_T kstring_t +typedef struct __kstring_t { + size_t l, m; + char *s; +} kstring_t; +#endif + +typedef struct { + uint64_t tab[4]; + int sep, finished; + const char *p; // end of the current token +} ks_tokaux_t; + +#ifdef __cplusplus +extern "C" { +#endif + + int ksprintf(kstring_t *s, const char *fmt, ...); + int ksprintf_fast(kstring_t *s, const char *fmt, ...); + int ksplit_core(char *s, int delimiter, int *_max, int **_offsets); + char *kstrstr(const char *str, const char *pat, int **_prep); + char *kstrnstr(const char *str, const char *pat, int n, int **_prep); + void *kmemmem(const void *_str, int n, const void *_pat, int m, int **_prep); + + /* kstrtok() is similar to strtok_r() except that str is not + * modified and both str and sep can be NULL. For efficiency, it is + * actually recommended to set both to NULL in the subsequent calls + * if sep is not changed. */ + char *kstrtok(const char *str, const char *sep, ks_tokaux_t *aux); + +#ifdef __cplusplus +} +#endif + +static inline void ks_resize(kstring_t *s, size_t size) +{ + if (s->m < size) { + s->m = size; + kroundup32(s->m); + s->s = (char*)realloc(s->s, s->m); + } +} + +static inline int kputsn(const char *p, int l, kstring_t *s) +{ + if (s->l + l + 1 >= s->m) { + s->m = s->l + l + 2; + kroundup32(s->m); + s->s = (char*)realloc(s->s, s->m); + } + memcpy(s->s + s->l, p, l); + s->l += l; + s->s[s->l] = 0; + return l; +} + +static inline int kputs(const char *p, kstring_t *s) +{ + return kputsn(p, strlen(p), s); +} + +static inline int kputc(int c, kstring_t *s) +{ + if (s->l + 1 >= s->m) { + s->m = s->l + 2; + kroundup32(s->m); + s->s = (char*)realloc(s->s, s->m); + } + s->s[s->l++] = c; + s->s[s->l] = 0; + return c; +} + +static inline int kputw(int c, kstring_t *s) +{ + char buf[16]; + int l, x; + if (c == 0) return kputc('0', s); + for (l = 0, x = c < 0? -c : c; x > 0; x /= 10) buf[l++] = x%10 + '0'; + if (c < 0) buf[l++] = '-'; + if (s->l + l + 1 >= s->m) { + s->m = s->l + l + 2; + kroundup32(s->m); + s->s = (char*)realloc(s->s, s->m); + } + for (x = l - 1; x >= 0; --x) s->s[s->l++] = buf[x]; + s->s[s->l] = 0; + return 0; +} + +static inline int kputuw(unsigned c, kstring_t *s) +{ + char buf[16]; + int l, i; + unsigned x; + if (c == 0) return kputc('0', s); + for (l = 0, x = c; x > 0; x /= 10) buf[l++] = x%10 + '0'; + if (s->l + l + 1 >= s->m) { + s->m = s->l + l + 2; + kroundup32(s->m); + s->s = (char*)realloc(s->s, s->m); + } + for (i = l - 1; i >= 0; --i) s->s[s->l++] = buf[i]; + s->s[s->l] = 0; + return 0; +} + +static inline int kputl(long c, kstring_t *s) +{ + char buf[32]; + long l, x; + if (c == 0) return kputc('0', s); + for (l = 0, x = c < 0? -c : c; x > 0; x /= 10) buf[l++] = x%10 + '0'; + if (c < 0) buf[l++] = '-'; + if (s->l + l + 1 >= s->m) { + s->m = s->l + l + 2; + kroundup32(s->m); + s->s = (char*)realloc(s->s, s->m); + } + for (x = l - 1; x >= 0; --x) s->s[s->l++] = buf[x]; + s->s[s->l] = 0; + return 0; +} + +static inline int *ksplit(kstring_t *s, int delimiter, int *n) +{ + int max = 0, *offsets = 0; + *n = ksplit_core(s->s, delimiter, &max, &offsets); + return offsets; +} + +#endif diff --git a/ksw.c b/ksw.c new file mode 100644 index 0000000..71f2635 --- /dev/null +++ b/ksw.c @@ -0,0 +1,352 @@ +/* The MIT License + + Copyright (c) 2011 by Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include +#include +#include +#include "ksw.h" + +#ifdef __GNUC__ +#define LIKELY(x) __builtin_expect((x),1) +#define UNLIKELY(x) __builtin_expect((x),0) +#else +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif + +const kswr_t g_defr = { 0, -1, -1, -1, -1, -1, -1 }; + +struct _kswq_t { + int qlen, slen; + uint8_t shift, mdiff, max, size; + __m128i *qp, *H0, *H1, *E, *Hmax; +}; + +/** + * Initialize the query data structure + * + * @param size Number of bytes used to store a score; valid valures are 1 or 2 + * @param qlen Length of the query sequence + * @param query Query sequence + * @param m Size of the alphabet + * @param mat Scoring matrix in a one-dimension array + * + * @return Query data structure + */ +kswq_t *ksw_qinit(int size, int qlen, const uint8_t *query, int m, const int8_t *mat) +{ + kswq_t *q; + int slen, a, tmp, p; + + size = size > 1? 2 : 1; + p = 8 * (3 - size); // # values per __m128i + slen = (qlen + p - 1) / p; // segmented length + q = (kswq_t*)malloc(sizeof(kswq_t) + 256 + 16 * slen * (m + 4)); // a single block of memory + q->qp = (__m128i*)(((size_t)q + sizeof(kswq_t) + 15) >> 4 << 4); // align memory + q->H0 = q->qp + slen * m; + q->H1 = q->H0 + slen; + q->E = q->H1 + slen; + q->Hmax = q->E + slen; + q->slen = slen; q->qlen = qlen; q->size = size; + // compute shift + tmp = m * m; + for (a = 0, q->shift = 127, q->mdiff = 0; a < tmp; ++a) { // find the minimum and maximum score + if (mat[a] < (int8_t)q->shift) q->shift = mat[a]; + if (mat[a] > (int8_t)q->mdiff) q->mdiff = mat[a]; + } + q->max = q->mdiff; + q->shift = 256 - q->shift; // NB: q->shift is uint8_t + q->mdiff += q->shift; // this is the difference between the min and max scores + // An example: p=8, qlen=19, slen=3 and segmentation: + // {{0,3,6,9,12,15,18,-1},{1,4,7,10,13,16,-1,-1},{2,5,8,11,14,17,-1,-1}} + if (size == 1) { + int8_t *t = (int8_t*)q->qp; + for (a = 0; a < m; ++a) { + int i, k, nlen = slen * p; + const int8_t *ma = mat + a * m; + for (i = 0; i < slen; ++i) + for (k = i; k < nlen; k += slen) // p iterations + *t++ = (k >= qlen? 0 : ma[query[k]]) + q->shift; + } + } else { + int16_t *t = (int16_t*)q->qp; + for (a = 0; a < m; ++a) { + int i, k, nlen = slen * p; + const int8_t *ma = mat + a * m; + for (i = 0; i < slen; ++i) + for (k = i; k < nlen; k += slen) // p iterations + *t++ = (k >= qlen? 0 : ma[query[k]]); + } + } + return q; +} + +kswr_t ksw_u8(kswq_t *q, int tlen, const uint8_t *target, int _gapo, int _gape, int xtra) // the first gap costs -(_o+_e) +{ + int slen, i, m_b, n_b, te = -1, gmax = 0, minsc, endsc; + uint64_t *b; + __m128i zero, gapoe, gape, shift, *H0, *H1, *E, *Hmax; + kswr_t r; + +#define __max_16(ret, xx) do { \ + (xx) = _mm_max_epu8((xx), _mm_srli_si128((xx), 8)); \ + (xx) = _mm_max_epu8((xx), _mm_srli_si128((xx), 4)); \ + (xx) = _mm_max_epu8((xx), _mm_srli_si128((xx), 2)); \ + (xx) = _mm_max_epu8((xx), _mm_srli_si128((xx), 1)); \ + (ret) = _mm_extract_epi16((xx), 0) & 0x00ff; \ + } while (0) + + // initialization + r = g_defr; + minsc = (xtra&KSW_XSUBO)? xtra&0xffff : 0x10000; + endsc = (xtra&KSW_XSTOP)? xtra&0xffff : 0x10000; + m_b = n_b = 0; b = 0; + zero = _mm_set1_epi32(0); + gapoe = _mm_set1_epi8(_gapo + _gape); + gape = _mm_set1_epi8(_gape); + shift = _mm_set1_epi8(q->shift); + H0 = q->H0; H1 = q->H1; E = q->E; Hmax = q->Hmax; + slen = q->slen; + for (i = 0; i < slen; ++i) { + _mm_store_si128(E + i, zero); + _mm_store_si128(H0 + i, zero); + _mm_store_si128(Hmax + i, zero); + } + // the core loop + for (i = 0; i < tlen; ++i) { + int j, k, cmp, imax; + __m128i e, h, f = zero, max = zero, *S = q->qp + target[i] * slen; // s is the 1st score vector + h = _mm_load_si128(H0 + slen - 1); // h={2,5,8,11,14,17,-1,-1} in the above example + h = _mm_slli_si128(h, 1); // h=H(i-1,-1); << instead of >> because x64 is little-endian + for (j = 0; LIKELY(j < slen); ++j) { + /* SW cells are computed in the following order: + * H(i,j) = max{H(i-1,j-1)+S(i,j), E(i,j), F(i,j)} + * E(i+1,j) = max{H(i,j)-q, E(i,j)-r} + * F(i,j+1) = max{H(i,j)-q, F(i,j)-r} + */ + // compute H'(i,j); note that at the beginning, h=H'(i-1,j-1) + h = _mm_adds_epu8(h, _mm_load_si128(S + j)); + h = _mm_subs_epu8(h, shift); // h=H'(i-1,j-1)+S(i,j) + e = _mm_load_si128(E + j); // e=E'(i,j) + h = _mm_max_epu8(h, e); + h = _mm_max_epu8(h, f); // h=H'(i,j) + max = _mm_max_epu8(max, h); // set max + _mm_store_si128(H1 + j, h); // save to H'(i,j) + // now compute E'(i+1,j) + h = _mm_subs_epu8(h, gapoe); // h=H'(i,j)-gapo + e = _mm_subs_epu8(e, gape); // e=E'(i,j)-gape + e = _mm_max_epu8(e, h); // e=E'(i+1,j) + _mm_store_si128(E + j, e); // save to E'(i+1,j) + // now compute F'(i,j+1) + f = _mm_subs_epu8(f, gape); + f = _mm_max_epu8(f, h); + // get H'(i-1,j) and prepare for the next j + h = _mm_load_si128(H0 + j); // h=H'(i-1,j) + } + // NB: we do not need to set E(i,j) as we disallow adjecent insertion and then deletion + for (k = 0; LIKELY(k < 16); ++k) { // this block mimics SWPS3; NB: H(i,j) updated in the lazy-F loop cannot exceed max + f = _mm_slli_si128(f, 1); + for (j = 0; LIKELY(j < slen); ++j) { + h = _mm_load_si128(H1 + j); + h = _mm_max_epu8(h, f); // h=H'(i,j) + _mm_store_si128(H1 + j, h); + h = _mm_subs_epu8(h, gapoe); + f = _mm_subs_epu8(f, gape); + cmp = _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_subs_epu8(f, h), zero)); + if (UNLIKELY(cmp == 0xffff)) goto end_loop16; + } + } +end_loop16: + //int k;for (k=0;k<16;++k)printf("%d ", ((uint8_t*)&max)[k]);printf("\n"); + __max_16(imax, max); // imax is the maximum number in max + if (imax >= minsc) { // write the b array; this condition adds branching unfornately + if (n_b == 0 || (int32_t)b[n_b-1] + 1 != i) { // then append + if (n_b == m_b) { + m_b = m_b? m_b<<1 : 8; + b = (uint64_t*)realloc(b, 8 * m_b); + } + b[n_b++] = (uint64_t)imax<<32 | i; + } else if ((int)(b[n_b-1]>>32) < imax) b[n_b-1] = (uint64_t)imax<<32 | i; // modify the last + } + if (imax > gmax) { + gmax = imax; te = i; // te is the end position on the target + for (j = 0; LIKELY(j < slen); ++j) // keep the H1 vector + _mm_store_si128(Hmax + j, _mm_load_si128(H1 + j)); + if (gmax + q->shift >= 255 || gmax >= endsc) break; + } + S = H1; H1 = H0; H0 = S; // swap H0 and H1 + } + r.score = gmax + q->shift < 255? gmax : 255; + r.te = te; + if (r.score != 255) { // get a->qe, the end of query match; find the 2nd best score + int max = -1, low, high, qlen = slen * 16; + uint8_t *t = (uint8_t*)Hmax; + for (i = 0; i < qlen; ++i, ++t) + if ((int)*t > max) max = *t, r.qe = i / 16 + i % 16 * slen; + //printf("%d,%d\n", max, gmax); + if (b) { + i = (r.score + q->max - 1) / q->max; + low = te - i; high = te + i; + for (i = 0; i < n_b; ++i) { + int e = (int32_t)b[i]; + if ((e < low || e > high) && b[i]>>32 > (uint32_t)r.score2) + r.score2 = b[i]>>32, r.te2 = e; + } + } + } + free(b); + return r; +} + +kswr_t ksw_i16(kswq_t *q, int tlen, const uint8_t *target, int _gapo, int _gape, int xtra) // the first gap costs -(_o+_e) +{ + int slen, i, m_b, n_b, te = -1, gmax = 0, minsc, endsc; + uint64_t *b; + __m128i zero, gapoe, gape, *H0, *H1, *E, *Hmax; + kswr_t r; + +#define __max_8(ret, xx) do { \ + (xx) = _mm_max_epi16((xx), _mm_srli_si128((xx), 8)); \ + (xx) = _mm_max_epi16((xx), _mm_srli_si128((xx), 4)); \ + (xx) = _mm_max_epi16((xx), _mm_srli_si128((xx), 2)); \ + (ret) = _mm_extract_epi16((xx), 0); \ + } while (0) + + // initialization + r = g_defr; + minsc = (xtra&KSW_XSUBO)? xtra&0xffff : 0x10000; + endsc = (xtra&KSW_XSTOP)? xtra&0xffff : 0x10000; + m_b = n_b = 0; b = 0; + zero = _mm_set1_epi32(0); + gapoe = _mm_set1_epi16(_gapo + _gape); + gape = _mm_set1_epi16(_gape); + H0 = q->H0; H1 = q->H1; E = q->E; Hmax = q->Hmax; + slen = q->slen; + for (i = 0; i < slen; ++i) { + _mm_store_si128(E + i, zero); + _mm_store_si128(H0 + i, zero); + _mm_store_si128(Hmax + i, zero); + } + // the core loop + for (i = 0; i < tlen; ++i) { + int j, k, imax; + __m128i e, h, f = zero, max = zero, *S = q->qp + target[i] * slen; // s is the 1st score vector + h = _mm_load_si128(H0 + slen - 1); // h={2,5,8,11,14,17,-1,-1} in the above example + h = _mm_slli_si128(h, 2); + for (j = 0; LIKELY(j < slen); ++j) { + h = _mm_adds_epi16(h, *S++); + e = _mm_load_si128(E + j); + h = _mm_max_epi16(h, e); + h = _mm_max_epi16(h, f); + max = _mm_max_epi16(max, h); + _mm_store_si128(H1 + j, h); + h = _mm_subs_epu16(h, gapoe); + e = _mm_subs_epu16(e, gape); + e = _mm_max_epi16(e, h); + _mm_store_si128(E + j, e); + f = _mm_subs_epu16(f, gape); + f = _mm_max_epi16(f, h); + h = _mm_load_si128(H0 + j); + } + for (k = 0; LIKELY(k < 16); ++k) { + f = _mm_slli_si128(f, 2); + for (j = 0; LIKELY(j < slen); ++j) { + h = _mm_load_si128(H1 + j); + h = _mm_max_epi16(h, f); + _mm_store_si128(H1 + j, h); + h = _mm_subs_epu16(h, gapoe); + f = _mm_subs_epu16(f, gape); + if(UNLIKELY(!_mm_movemask_epi8(_mm_cmpgt_epi16(f, h)))) goto end_loop8; + } + } +end_loop8: + __max_8(imax, max); + if (imax >= minsc) { + if (n_b == 0 || (int32_t)b[n_b-1] + 1 != i) { + if (n_b == m_b) { + m_b = m_b? m_b<<1 : 8; + b = (uint64_t*)realloc(b, 8 * m_b); + } + b[n_b++] = (uint64_t)imax<<32 | i; + } else if ((int)(b[n_b-1]>>32) < imax) b[n_b-1] = (uint64_t)imax<<32 | i; // modify the last + } + if (imax > gmax) { + gmax = imax; te = i; + for (j = 0; LIKELY(j < slen); ++j) + _mm_store_si128(Hmax + j, _mm_load_si128(H1 + j)); + if (gmax >= endsc) break; + } + S = H1; H1 = H0; H0 = S; + } + r.score = gmax; r.te = te; + { + int max = -1, low, high, qlen = slen * 8; + uint16_t *t = (uint16_t*)Hmax; + for (i = 0, r.qe = -1; i < qlen; ++i, ++t) + if ((int)*t > max) max = *t, r.qe = i / 8 + i % 8 * slen; + if (b) { + i = (r.score + q->max - 1) / q->max; + low = te - i; high = te + i; + for (i = 0; i < n_b; ++i) { + int e = (int32_t)b[i]; + if ((e < low || e > high) && b[i]>>32 > (uint32_t)r.score2) + r.score2 = b[i]>>32, r.te2 = e; + } + } + } + free(b); + return r; +} + +static void revseq(int l, uint8_t *s) +{ + int i, t; + for (i = 0; i < l>>1; ++i) + t = s[i], s[i] = s[l - 1 - i], s[l - 1 - i] = t; +} + +kswr_t ksw_align(int qlen, uint8_t *query, int tlen, uint8_t *target, int m, const int8_t *mat, int gapo, int gape, int xtra, kswq_t **qry) +{ + int size; + kswq_t *q; + kswr_t r, rr; + kswr_t (*func)(kswq_t*, int, const uint8_t*, int, int, int); + + q = (qry && *qry)? *qry : ksw_qinit((xtra&KSW_XBYTE)? 1 : 2, qlen, query, m, mat); + if (qry && *qry == 0) *qry = q; + func = q->size == 2? ksw_i16 : ksw_u8; + size = q->size; + r = func(q, tlen, target, gapo, gape, xtra); + if (qry == 0) free(q); + if ((xtra&KSW_XSTART) == 0 || ((xtra&KSW_XSUBO) && r.score < (xtra&0xffff))) return r; + revseq(r.qe + 1, query); revseq(r.te + 1, target); // +1 because qe/te points to the exact end, not the position after the end + q = ksw_qinit(size, r.qe + 1, query, m, mat); + rr = func(q, tlen, target, gapo, gape, KSW_XSTOP | r.score); + revseq(r.qe + 1, query); revseq(r.te + 1, target); + free(q); + if (r.score == rr.score) + r.tb = r.te - rr.te, r.qb = r.qe - rr.qe; + return r; +} diff --git a/ksw.h b/ksw.h new file mode 100644 index 0000000..160cee8 --- /dev/null +++ b/ksw.h @@ -0,0 +1,71 @@ +#ifndef __AC_KSW_H +#define __AC_KSW_H + +#include + +#define KSW_XBYTE 0x10000 +#define KSW_XSTOP 0x20000 +#define KSW_XSUBO 0x40000 +#define KSW_XSTART 0x80000 + +struct _kswq_t; +typedef struct _kswq_t kswq_t; + +typedef struct { + int score; // best score + int te, qe; // target end and query end + int score2, te2; // second best score and ending position on the target + int tb, qb; // target start and query start +} kswr_t; + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * Aligning two sequences + * + * @param qlen length of the query sequence (typically +#include +#include + +/************ + * kt_for() * + ************/ + +struct kt_for_t; + +typedef struct { + struct kt_for_t *t; + long i; +} ktf_worker_t; + +typedef struct kt_for_t { + int n_threads; + long n; + ktf_worker_t *w; + void (*func)(void*,long,int); + void *data; +} kt_for_t; + +static inline long steal_work(kt_for_t *t) +{ + int i, min_i = -1; + long k, min = LONG_MAX; + for (i = 0; i < t->n_threads; ++i) + if (min > t->w[i].i) min = t->w[i].i, min_i = i; + k = __sync_fetch_and_add(&t->w[min_i].i, t->n_threads); + return k >= t->n? -1 : k; +} + +static void *ktf_worker(void *data) +{ + ktf_worker_t *w = (ktf_worker_t*)data; + long i; + for (;;) { + i = __sync_fetch_and_add(&w->i, w->t->n_threads); + if (i >= w->t->n) break; + w->t->func(w->t->data, i, w - w->t->w); + } + while ((i = steal_work(w->t)) >= 0) + w->t->func(w->t->data, i, w - w->t->w); + pthread_exit(0); +} + +void kt_for(int n_threads, void (*func)(void*,long,int), void *data, long n) +{ + if (n_threads > 1) { + int i; + kt_for_t t; + pthread_t *tid; + t.func = func, t.data = data, t.n_threads = n_threads, t.n = n; + t.w = (ktf_worker_t*)alloca(n_threads * sizeof(ktf_worker_t)); + tid = (pthread_t*)alloca(n_threads * sizeof(pthread_t)); + for (i = 0; i < n_threads; ++i) + t.w[i].t = &t, t.w[i].i = i; + for (i = 0; i < n_threads; ++i) pthread_create(&tid[i], 0, ktf_worker, &t.w[i]); + for (i = 0; i < n_threads; ++i) pthread_join(tid[i], 0); + } else { + long j; + for (j = 0; j < n; ++j) func(data, j, 0); + } +} diff --git a/kvec.h b/kvec.h new file mode 100644 index 0000000..632fce4 --- /dev/null +++ b/kvec.h @@ -0,0 +1,110 @@ +/* The MIT License + + Copyright (c) 2008, by Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + An example: + +#include "kvec.h" +int main() { + kvec_t(int) array; + kv_init(array); + kv_push(int, array, 10); // append + kv_a(int, array, 20) = 5; // dynamic + kv_A(array, 20) = 4; // static + kv_destroy(array); + return 0; +} +*/ + +/* + 2008-09-22 (0.1.0): + + * The initial version. + +*/ + +#ifndef AC_KVEC_H +#define AC_KVEC_H + +#include + +#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) + +#define kvec_t(type) struct { size_t n, m; type *a; } +#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0) +#define kv_destroy(v) free((v).a) +#define kv_A(v, i) ((v).a[(i)]) +#define kv_pop(v) ((v).a[--(v).n]) +#define kv_size(v) ((v).n) +#define kv_max(v) ((v).m) + +#define kv_resize(type, v, s) do { \ + if ((v).m < (s)) { \ + (v).m = (s); \ + kv_roundup32((v).m); \ + (v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \ + } \ + } while (0) + +#define kv_copy(type, v1, v0) do { \ + if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \ + (v1).n = (v0).n; \ + memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \ + } while (0) \ + +#define kv_push(type, v, x) do { \ + if ((v).n == (v).m) { \ + (v).m = (v).m? (v).m<<1 : 2; \ + (v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \ + } \ + (v).a[(v).n++] = (x); \ + } while (0) + +#define kv_pushp(type, v, p) do { \ + if ((v).n == (v).m) { \ + (v).m = (v).m? (v).m<<1 : 2; \ + (v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \ + } \ + *(p) = &(v).a[(v).n++]; \ + } while (0) + +#define kv_a(type, v, i) ((v).m <= (size_t)(i)? \ + ((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \ + (v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \ + : (v).n <= (size_t)(i)? (v).n = (i) \ + : 0), (v).a[(i)] + +#define kv_reverse(type, v, start) do { \ + if ((v).m > 0 && (v).n > (start)) { \ + size_t __i, __end = (v).n - (start); \ + type *__a = (v).a + (start); \ + for (__i = 0; __i < __end>>1; ++__i) { \ + type __t = __a[__end - 1 - __i]; \ + __a[__end - 1 - __i] = __a[__i]; __a[__i] = __t; \ + } \ + } \ + } while (0) + +#endif diff --git a/mag.c b/mag.c new file mode 100644 index 0000000..acdca29 --- /dev/null +++ b/mag.c @@ -0,0 +1,620 @@ +/* remaining problems: + + 1. multiedges due to tandem repeats +*/ + +#include +#include +#include +#include +#include "mag.h" +#include "kvec.h" +#include "internal.h" +#include "kseq.h" +KSEQ_DECLARE(gzFile) + +#include "khash.h" +KHASH_INIT2(64,, khint64_t, uint64_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +typedef khash_t(64) hash64_t; + +#define ku128_xlt(a, b) ((a).x < (b).x || ((a).x == (b).x && (a).y > (b).y)) +#define ku128_ylt(a, b) ((int64_t)(a).y > (int64_t)(b).y) +#include "ksort.h" +KSORT_INIT(128x, ku128_t, ku128_xlt) +KSORT_INIT(128y, ku128_t, ku128_ylt) +KSORT_INIT_GENERIC(uint64_t) + +#define edge_mark_del(_x) ((_x).x = (uint64_t)-2, (_x).y = 0) +#define edge_is_del(_x) ((_x).x == (uint64_t)-2 || (_x).y == 0) + +int fm_verbose = 1; + +/********************* + * Vector operations * + *********************/ + +static inline void v128_clean(ku128_v *r) +{ + int i, j; + for (i = j = 0; i < r->n; ++i) + if (!edge_is_del(r->a[i])) { // keep this arc + if (j != i) r->a[j++] = r->a[i]; + else ++j; + } + r->n = j; +} + +void mag_v128_clean(ku128_v *r) +{ + v128_clean(r); +} + +static inline void v128_rmdup(ku128_v *r) +{ + int l, cnt; + uint64_t x; + if (r->n > 1) ks_introsort(128x, r->n, r->a); + for (l = cnt = 0; l < r->n; ++l) // jump to the first node to be retained + if (edge_is_del(r->a[l])) ++cnt; + else break; + if (l == r->n) { // no good arcs + r->n = 0; + return; + } + x = r->a[l].x; + for (++l; l < r->n; ++l) { // mark duplicated node + if (edge_is_del(r->a[l]) || r->a[l].x == x) + edge_mark_del(r->a[l]), ++cnt; + else x = r->a[l].x; + } + if (cnt) v128_clean(r); +} + +static inline void v128_cap(ku128_v *r, int max) +{ + int i, thres; + if (r->n <= max) return; + ks_introsort(128y, r->n, r->a); + thres = r->a[max].y; + for (i = 0; i < r->n; ++i) + if (r->a[i].y == thres) break; + r->n = i; +} + +/************************************************* + * Mapping between vertex id and interval end id * + *************************************************/ + +void mag_g_build_hash(mag_t *g) +{ + long i; + int j, ret; + hash64_t *h; + h = kh_init(64); + for (i = 0; i < g->v.n; ++i) { + const magv_t *p = &g->v.a[i]; + for (j = 0; j < 2; ++j) { + khint_t k = kh_put(64, h, p->k[j], &ret); + if (ret == 0) { + if (fm_verbose >= 2) + fprintf(stderr, "[W::%s] terminal %ld is duplicated.\n", __func__, (long)p->k[j]); + kh_val(h, k) = (uint64_t)-1; + } else kh_val(h, k) = i<<1|j; + } + } + g->h = h; +} + +static inline uint64_t tid2idd(hash64_t *h, uint64_t tid) +{ + khint_t k = kh_get(64, h, tid); + assert(k != kh_end(h)); + return kh_val(h, k); +} + +uint64_t mag_tid2idd(void *h, uint64_t tid) // exported version +{ + return tid2idd(h, tid); +} + +void mag_g_amend(mag_t *g) +{ + int i, j, l, ll; + for (i = 0; i < g->v.n; ++i) { + magv_t *p = &g->v.a[i]; + ku128_v *r; + for (j = 0; j < 2; ++j) { + for (l = 0; l < p->nei[j].n; ++l) { + khint_t k; + uint64_t z, x = p->nei[j].a[l].x; + k = kh_get(64, g->h, x); + if (k == kh_end((hash64_t*)g->h)) { // neighbor is not in the hash table; likely due to tip removal + edge_mark_del(p->nei[j].a[l]); + continue; + } else z = kh_val((hash64_t*)g->h, k); + r = &g->v.a[z>>1].nei[z&1]; + for (ll = 0, z = p->k[j]; ll < r->n; ++ll) + if (r->a[ll].x == z) break; + if (ll == r->n) // not in neighbor's neighor + edge_mark_del(p->nei[j].a[l]); + } + v128_rmdup(&p->nei[j]); + } + } +} + +/********************************* + * Graph I/O initialization etc. * + *********************************/ + +void mag_v_write(const magv_t *p, kstring_t *out) +{ + int j, k; + if (p->len <= 0) return; + out->l = 0; + kputc('@', out); kputl(p->k[0], out); kputc(':', out); kputl(p->k[1], out); + kputc('\t', out); kputw(p->nsr, out); + for (j = 0; j < 2; ++j) { + const ku128_v *r = &p->nei[j]; + kputc('\t', out); + for (k = 0; k < r->n; ++k) { + if (edge_is_del(r->a[k])) continue; + kputl(r->a[k].x, out); kputc(',', out); kputw((int32_t)r->a[k].y, out); + kputc(';', out); + } + if (p->nei[j].n == 0) kputc('.', out); + } + kputc('\n', out); + ks_resize(out, out->l + 2 * p->len + 5); + for (j = 0; j < p->len; ++j) + out->s[out->l++] = "ACGT"[(int)p->seq[j] - 1]; + out->s[out->l] = 0; + kputsn("\n+\n", 3, out); + kputsn(p->cov, p->len, out); + kputc('\n', out); +} + +void mag_g_print(const mag_t *g) +{ + int i; + kstring_t out; + out.l = out.m = 0; out.s = 0; + for (i = 0; i < g->v.n; ++i) { + if (g->v.a[i].len < 0) continue; + mag_v_write(&g->v.a[i], &out); + fwrite(out.s, 1, out.l, stdout); + } + free(out.s); + fflush(stdout); +} + +/************************** + * Basic graph operations * + **************************/ + +void mag_v_destroy(magv_t *v) +{ + free(v->nei[0].a); free(v->nei[1].a); + free(v->seq); free(v->cov); + memset(v, 0, sizeof(magv_t)); + v->len = -1; +} + +void mag_g_destroy(mag_t *g) +{ + int i; + kh_destroy(64, g->h); + for (i = 0; i < g->v.n; ++i) + mag_v_destroy(&g->v.a[i]); + free(g->v.a); + free(g); +} + +void mag_v_copy_to_empty(magv_t *dst, const magv_t *src) // NB: memory leak if dst is allocated +{ + memcpy(dst, src, sizeof(magv_t)); + dst->max_len = dst->len + 1; + kroundup32(dst->max_len); + dst->seq = calloc(dst->max_len, 1); memcpy(dst->seq, src->seq, src->len); + dst->cov = calloc(dst->max_len, 1); memcpy(dst->cov, src->cov, src->len); + kv_init(dst->nei[0]); kv_copy(ku128_t, dst->nei[0], src->nei[0]); + kv_init(dst->nei[1]); kv_copy(ku128_t, dst->nei[1], src->nei[1]); +} + +void mag_eh_add(mag_t *g, uint64_t u, uint64_t v, int ovlp) // add v to u +{ + ku128_v *r; + ku128_t *q; + uint64_t idd; + int i; + if ((int64_t)u < 0) return; + idd = tid2idd(g->h, u); + r = &g->v.a[idd>>1].nei[idd&1]; + for (i = 0; i < r->n; ++i) // no multi-edges + if (r->a[i].x == v) return; + kv_pushp(ku128_t, *r, &q); + q->x = v; q->y = ovlp; +} + +void mag_eh_markdel(mag_t *g, uint64_t u, uint64_t v) // mark deletion of v from u +{ + int i; + uint64_t idd; + if ((int64_t)u < 0) return; + idd = tid2idd(g->h, u); + ku128_v *r = &g->v.a[idd>>1].nei[idd&1]; + for (i = 0; i < r->n; ++i) + if (r->a[i].x == v) edge_mark_del(r->a[i]); +} + +void mag_v_del(mag_t *g, magv_t *p) +{ + int i, j; + khint_t k; + if (p->len < 0) return; + for (i = 0; i < 2; ++i) { + ku128_v *r = &p->nei[i]; + for (j = 0; j < r->n; ++j) + if (!edge_is_del(r->a[j]) && r->a[j].x != p->k[0] && r->a[j].x != p->k[1]) + mag_eh_markdel(g, r->a[j].x, p->k[i]); + } + for (i = 0; i < 2; ++i) { + k = kh_get(64, g->h, p->k[i]); + kh_del(64, g->h, k); + } + mag_v_destroy(p); +} + +void mag_v_transdel(mag_t *g, magv_t *p, int min_ovlp) +{ + if (p->nei[0].n && p->nei[1].n) { + int i, j, ovlp; + for (i = 0; i < p->nei[0].n; ++i) { + if (edge_is_del(p->nei[0].a[i]) || p->nei[0].a[i].x == p->k[0] || p->nei[0].a[i].x == p->k[1]) continue; // due to p->p loop + for (j = 0; j < p->nei[1].n; ++j) { + if (edge_is_del(p->nei[1].a[j]) || p->nei[1].a[j].x == p->k[0] || p->nei[1].a[j].x == p->k[1]) continue; + ovlp = (int)(p->nei[0].a[i].y + p->nei[1].a[j].y) - p->len; + if (ovlp >= min_ovlp) { + mag_eh_add(g, p->nei[0].a[i].x, p->nei[1].a[j].x, ovlp); + mag_eh_add(g, p->nei[1].a[j].x, p->nei[0].a[i].x, ovlp); + } + } + } + } + mag_v_del(g, p); +} + +void mag_v_flip(mag_t *g, magv_t *p) +{ + ku128_v t; + khint_t k; + hash64_t *h = (hash64_t*)g->h; + + seq_revcomp6(p->len, (uint8_t*)p->seq); + seq_reverse(p->len, (uint8_t*)p->cov); + p->k[0] ^= p->k[1]; p->k[1] ^= p->k[0]; p->k[0] ^= p->k[1]; + t = p->nei[0]; p->nei[0] = p->nei[1]; p->nei[1] = t; + k = kh_get(64, h, p->k[0]); + assert(k != kh_end(h)); + kh_val(h, k) ^= 1; + k = kh_get(64, h, p->k[1]); + assert(k != kh_end(h)); + kh_val(h, k) ^= 1; +} + +/********************* + * Unambiguous merge * + *********************/ + +int mag_vh_merge_try(mag_t *g, magv_t *p, int min_merge_len) // merge p's neighbor to the right-end of p +{ + magv_t *q; + khint_t kp, kq; + int i, j, new_l; + hash64_t *h = (hash64_t*)g->h; + + // check if an unambiguous merge can be performed + if (p->nei[1].n != 1) return -1; // multiple or no neighbor; do not merge + if ((int64_t)p->nei[1].a[0].x < 0) return -2; // deleted neighbor + if ((int)p->nei[1].a[0].y < min_merge_len) return -5; + kq = kh_get(64, g->h, p->nei[1].a[0].x); + assert(kq != kh_end(h)); // otherwise the neighbor is non-existant + q = &g->v.a[kh_val((hash64_t*)g->h, kq)>>1]; + if (p == q) return -3; // we have a loop p->p. We cannot merge in this case + if (q->nei[kh_val(h, kq)&1].n != 1) return -4; // the neighbor q has multiple neighbors. cannot be an unambiguous merge + + // we can perform a merge; do further consistency check (mostly check bugs) + if (kh_val(h, kq)&1) mag_v_flip(g, q); // a "><" bidirectional arc; flip q + kp = kh_get(64, g->h, p->k[1]); assert(kp != kh_end(h)); // get the iterator to p + kh_del(64, g->h, kp); kh_del(64, g->h, kq); // remove the two ends of the arc in the hash table + assert(p->k[1] == q->nei[0].a[0].x && q->k[0] == p->nei[1].a[0].x); // otherwise inconsistent topology + assert(p->nei[1].a[0].y == q->nei[0].a[0].y); // the overlap length must be the same + assert(p->len >= p->nei[1].a[0].y && q->len >= p->nei[1].a[0].y); // and the overlap is shorter than both vertices + + // update the read count and sequence length + p->nsr += q->nsr; + new_l = p->len + q->len - p->nei[1].a[0].y; + if (new_l + 1 > p->max_len) { // then double p->seq and p->cov + p->max_len = new_l + 1; + kroundup32(p->max_len); + p->seq = realloc(p->seq, p->max_len); + p->cov = realloc(p->cov, p->max_len); + } + // merge seq and cov + for (i = p->len - p->nei[1].a[0].y, j = 0; j < q->len; ++i, ++j) { // write seq and cov + p->seq[i] = q->seq[j]; + if (i < p->len) { + if ((int)p->cov[i] + (q->cov[j] - 33) > 126) p->cov[i] = 126; + else p->cov[i] += q->cov[j] - 33; + } else p->cov[i] = q->cov[j]; + } + p->seq[new_l] = p->cov[new_l] = 0; + p->len = new_l; + // merge neighbors + free(p->nei[1].a); + p->nei[1] = q->nei[1]; p->k[1] = q->k[1]; + q->nei[1].a = 0; // to avoid freeing p->nei[1] by mag_v_destroy() below + // update the hash table for the right end of p + kp = kh_get(64, g->h, p->k[1]); + assert(kp != kh_end((hash64_t*)g->h)); + kh_val(h, kp) = (p - g->v.a)<<1 | 1; + // clean up q + mag_v_destroy(q); + return 0; +} + +void mag_g_merge(mag_t *g, int rmdup, int min_merge_len) +{ + int i; + uint64_t n = 0; + for (i = 0; i < g->v.n; ++i) { // remove multiedges; FIXME: should we do that? + if (rmdup) { + v128_rmdup(&g->v.a[i].nei[0]); + v128_rmdup(&g->v.a[i].nei[1]); + } else { + v128_clean(&g->v.a[i].nei[0]); + v128_clean(&g->v.a[i].nei[1]); + } + } + for (i = 0; i < g->v.n; ++i) { + magv_t *p = &g->v.a[i]; + if (p->len < 0) continue; + while (mag_vh_merge_try(g, p, min_merge_len) == 0) ++n; + mag_v_flip(g, p); + while (mag_vh_merge_try(g, p, min_merge_len) == 0) ++n; + } + if (fm_verbose >= 3) + fprintf(stderr, "[M::%s] unambiguously merged %ld pairs of vertices\n", __func__, (long)n); +} + +/***************************** + * Easy graph simplification * + *****************************/ + +typedef magv_t *magv_p; + +#define mag_vlt1(a, b) ((a)->nsr < (b)->nsr || ((a)->nsr == (b)->nsr && (a)->len < (b)->len)) +KSORT_INIT(vlt1, magv_p, mag_vlt1) + +#define mag_vlt2(a, b) ((a)->nei[0].n + (a)->nei[1].n < (b)->nei[0].n + (b)->nei[1].n) +KSORT_INIT(vlt2, magv_p, mag_vlt2) + +int mag_g_rm_vext(mag_t *g, int min_len, int min_nsr) +{ + int i; + kvec_t(magv_p) a = {0,0,0}; + + for (i = 0; i < g->v.n; ++i) { + magv_t *p = &g->v.a[i]; + if (p->len < 0 || (p->nei[0].n > 0 && p->nei[1].n > 0)) continue; + if (p->len >= min_len || p->nsr >= min_nsr) continue; + kv_push(magv_p, a, p); + } + ks_introsort(vlt1, a.n, a.a); + for (i = 0; i < a.n; ++i) mag_v_del(g, a.a[i]); + free(a.a); + if (fm_verbose >= 3) + fprintf(stderr, "[M::%s] removed %ld tips (min_len=%d, min_nsr=%d)\n", __func__, a.n, min_len, min_nsr); + return a.n; +} + +int mag_g_rm_vint(mag_t *g, int min_len, int min_nsr, int min_ovlp) +{ + int i; + kvec_t(magv_p) a = {0,0,0}; + + for (i = 0; i < g->v.n; ++i) { + magv_t *p = &g->v.a[i]; + if (p->len >= 0 && p->len < min_len && p->nsr < min_nsr) + kv_push(magv_p, a, p); + } + ks_introsort(vlt1, a.n, a.a); + for (i = 0; i < a.n; ++i) mag_v_transdel(g, a.a[i], min_ovlp); + free(a.a); + if (fm_verbose >= 3) + fprintf(stderr, "[M::%s] removed %ld internal vertices (min_len=%d, min_nsr=%d)\n", __func__, a.n, min_len, min_nsr); + return a.n; +} + +void mag_g_rm_edge(mag_t *g, int min_ovlp, double min_ratio, int min_len, int min_nsr) +{ + int i, j, k; + kvec_t(magv_p) a = {0,0,0}; + uint64_t n_marked = 0; + + for (i = 0; i < g->v.n; ++i) { + magv_t *p = &g->v.a[i]; + if (p->len < 0) continue; + if ((p->nei[0].n == 0 || p->nei[1].n == 0) && p->len < min_len && p->nsr < min_nsr) + continue; // skip tips + kv_push(magv_p, a, p); + } + ks_introsort(vlt1, a.n, a.a); + + for (i = a.n - 1; i >= 0; --i) { + magv_t *p = a.a[i]; + for (j = 0; j < 2; ++j) { + ku128_v *r = &p->nei[j]; + int max_ovlp = min_ovlp, max_k = -1; + if (r->n == 0) continue; // no overlapping reads + for (k = 0; k < r->n; ++k) // get the max overlap length + if (max_ovlp < r->a[k].y) + max_ovlp = r->a[k].y, max_k = k; + if (max_k >= 0) { // test if max_k is a tip + uint64_t x = tid2idd(g->h, r->a[max_k].x); + magv_t *q = &g->v.a[x>>1]; + if (q->len >= 0 && (q->nei[0].n == 0 || q->nei[1].n == 0) && q->len < min_len && q->nsr < min_nsr) + max_ovlp = min_ovlp; + } + for (k = 0; k < r->n; ++k) { + if (edge_is_del(r->a[k])) continue; + if (r->a[k].y < min_ovlp || (double)r->a[k].y / max_ovlp < min_ratio) { + mag_eh_markdel(g, r->a[k].x, p->k[j]); // FIXME: should we check if r->a[k] is p itself? + edge_mark_del(r->a[k]); + ++n_marked; + } + } + } + } + free(a.a); + if (fm_verbose >= 3) + fprintf(stderr, "[M::%s] removed %ld edges\n", __func__, (long)n_marked); +} + +/********************************************* + * A-statistics and simplistic flow analysis * + *********************************************/ + +#define A_THRES 20. +#define A_MIN_SUPP 5 + +double mag_cal_rdist(mag_t *g) +{ + magv_v *v = &g->v; + int j; + uint64_t *srt; + double rdist = -1.; + int64_t i, sum_n_all, sum_n, sum_l; + + srt = calloc(v->n, 8); + for (i = 0, sum_n_all = 0; i < v->n; ++i) { + srt[i] = (uint64_t)v->a[i].nsr<<32 | i; + sum_n_all += v->a[i].nsr; + } + ks_introsort_uint64_t(v->n, srt); + + for (j = 0; j < 2; ++j) { + sum_n = sum_l = 0; + for (i = v->n - 1; i >= 0; --i) { + const magv_t *p = &v->a[srt[i]<<32>>32]; + int tmp1, tmp2; + tmp1 = tmp2 = 0; + if (p->nei[0].n) ++tmp1, tmp2 += p->nei[0].a[0].y; + if (p->nei[1].n) ++tmp1, tmp2 += p->nei[1].a[0].y; + if (tmp1) tmp2 /= tmp1; + if (rdist > 0.) { + double A = (p->len - tmp1) / rdist - p->nsr * M_LN2; + if (A < A_THRES) continue; + } + sum_n += p->nsr; + sum_l += p->len - tmp1; + if (sum_n >= sum_n_all * 0.5) break; + } + rdist = (double)sum_l / sum_n; + } + if (fm_verbose >= 3) { + fprintf(stderr, "[M::%s] average read distance %.3f.\n", __func__, rdist); + fprintf(stderr, "[M::%s] approximate genome size: %.0f (inaccurate!)\n", __func__, rdist * sum_n_all); + } + + free(srt); + return rdist; +} + +/************** + * Key portal * + **************/ + +void mag_init_opt(magopt_t *o) +{ + memset(o, 0, sizeof(magopt_t)); + o->trim_len = 0; + o->trim_depth = 6; + + o->min_elen = 300; + o->min_ovlp = 0; + o->min_merge_len = 0; + o->min_ensr = 4; + o->min_insr = 3; + o->min_dratio1 = 0.7; + + o->max_bcov = 10.; + o->max_bfrac = 0.15; + o->max_bvtx = 64; + o->max_bdist = 512; + o->max_bdiff = 50; +} + +void mag_g_clean(mag_t *g, const magopt_t *opt) +{ + int j; + + if (g->min_ovlp < opt->min_ovlp) g->min_ovlp = opt->min_ovlp; + for (j = 2; j <= opt->min_ensr; ++j) + mag_g_rm_vext(g, opt->min_elen, j); + mag_g_merge(g, 0, opt->min_merge_len); + mag_g_rm_edge(g, g->min_ovlp, opt->min_dratio1, opt->min_elen, opt->min_ensr); + mag_g_merge(g, 1, opt->min_merge_len); + for (j = 2; j <= opt->min_ensr; ++j) + mag_g_rm_vext(g, opt->min_elen, j); + mag_g_merge(g, 0, opt->min_merge_len); + if ((opt->flag & MAG_F_AGGRESSIVE) || (opt->flag & MAG_F_POPOPEN)) mag_g_pop_open(g, opt->min_elen); + if (!(opt->flag & MAG_F_NO_SIMPL)) mag_g_simplify_bubble(g, opt->max_bvtx, opt->max_bdist); + mag_g_pop_simple(g, opt->max_bcov, opt->max_bfrac, opt->min_merge_len, opt->max_bdiff, opt->flag & MAG_F_AGGRESSIVE); + mag_g_rm_vint(g, opt->min_elen, opt->min_insr, g->min_ovlp); + mag_g_rm_edge(g, g->min_ovlp, opt->min_dratio1, opt->min_elen, opt->min_ensr); + mag_g_merge(g, 1, opt->min_merge_len); + mag_g_rm_vext(g, opt->min_elen, opt->min_ensr); + mag_g_merge(g, 0, opt->min_merge_len); + if ((opt->flag & MAG_F_AGGRESSIVE) || (opt->flag & MAG_F_POPOPEN)) mag_g_pop_open(g, opt->min_elen); + mag_g_rm_vext(g, opt->min_elen, opt->min_ensr); + mag_g_merge(g, 0, opt->min_merge_len); +} + +void mag_v_trim_open(mag_t *g, magv_t *v, int trim_len, int trim_depth) +{ + int i, j, tl[2]; + if (v->nei[0].n > 0 && v->nei[1].n > 0) return; // no open end; do nothing + if (v->nei[0].n == 0 && v->nei[1].n == 0 && v->len < trim_len * 3) { // disconnected short vertex + mag_v_del(g, v); + return; + } + for (j = 0; j < 2; ++j) { + ku128_v *r = &v->nei[!j]; + int max_ovlp = 0; + for (i = 0; i < r->n; ++i) + max_ovlp = max_ovlp > r->a[i].y? max_ovlp : r->a[i].y; + tl[j] = v->len - max_ovlp < trim_len? v->len - max_ovlp : trim_len; + } + if (v->nei[0].n == 0) { + for (i = 0; i < tl[0] && v->cov[i] - 33 < trim_depth; ++i); + tl[0] = i; + v->len -= i; + memmove(v->seq, v->seq + tl[0], v->len); + memmove(v->cov, v->cov + tl[0], v->len); + } + if (v->nei[1].n == 0) { + for (i = v->len - 1; i >= v->len - tl[1] && v->cov[i] - 33 < trim_depth; --i); + tl[1] = v->len - 1 - i; + v->len -= tl[1]; + } +} + +void mag_g_trim_open(mag_t *g, const magopt_t *opt) +{ + int i; + if (opt->trim_len == 0) return; + for (i = 0; i < g->v.n; ++i) + mag_v_trim_open(g, &g->v.a[i], opt->trim_len, opt->trim_depth); +} diff --git a/mag.h b/mag.h new file mode 100644 index 0000000..ca76527 --- /dev/null +++ b/mag.h @@ -0,0 +1,69 @@ +#ifndef FM_MOG_H +#define FM_MOG_H + +#include +#include +#include "kstring.h" +#include "fml.h" + +#ifndef KINT_DEF +#define KINT_DEF +typedef struct { uint64_t x, y; } ku128_t; +typedef struct { size_t n, m; uint64_t *a; } ku64_v; +typedef struct { size_t n, m; ku128_t *a; } ku128_v; +#endif + +typedef struct { + int len, nsr; // length; number supporting reads + uint32_t max_len;// allocated seq/cov size + uint64_t k[2]; // bi-interval + ku128_v nei[2]; // neighbors + char *seq, *cov; // sequence and coverage + void *ptr; // additional information +} magv_t; + +typedef struct { size_t n, m; magv_t *a; } magv_v; + +typedef struct mag_t { + magv_v v; + float rdist; // read distance + int min_ovlp; // minimum overlap seen from the graph + void *h; +} mag_t; + +struct mogb_aux; +typedef struct mogb_aux mogb_aux_t; + +#ifdef __cplusplus +extern "C" { +#endif + + void mag_init_opt(magopt_t *o); + void mag_g_clean(mag_t *g, const magopt_t *opt); + + void mag_g_destroy(mag_t *g); + void mag_g_amend(mag_t *g); + void mag_g_build_hash(mag_t *g); + void mag_g_print(const mag_t *g); + int mag_g_rm_vext(mag_t *g, int min_len, int min_nsr); + void mag_g_rm_edge(mag_t *g, int min_ovlp, double min_ratio, int min_len, int min_nsr); + void mag_g_merge(mag_t *g, int rmdup, int min_merge_len); + void mag_g_simplify_bubble(mag_t *g, int max_vtx, int max_dist); + void mag_g_pop_simple(mag_t *g, float max_cov, float max_frac, int min_merge_len, int max_bdiff, int aggressive); + void mag_g_pop_open(mag_t *g, int min_elen); + void mag_g_trim_open(mag_t *g, const magopt_t *opt); + + void mag_v_copy_to_empty(magv_t *dst, const magv_t *src); // NB: memory leak if dst is allocated + void mag_v_del(mag_t *g, magv_t *p); + void mag_v_write(const magv_t *p, kstring_t *out); + void mag_v_pop_open(mag_t *g, magv_t *p, int min_elen); + + uint64_t mag_tid2idd(void *h, uint64_t tid); + void mag_v128_clean(ku128_v *r); + double mag_cal_rdist(mag_t *g); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/misc.c b/misc.c new file mode 100644 index 0000000..6362000 --- /dev/null +++ b/misc.c @@ -0,0 +1,302 @@ +#include +#include "internal.h" +#include "kstring.h" +#include "rle.h" +#include "mrope.h" +#include "rld0.h" +#include "mag.h" +#include "kvec.h" +#include "fml.h" +#include "htab.h" + +unsigned char seq_nt6_table[256] = { + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 1, 5, 2, 5, 5, 5, 3, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 1, 5, 2, 5, 5, 5, 3, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 +}; + +void fml_opt_init(fml_opt_t *opt) +{ + opt->n_threads = 1; + opt->ec_k = 0; + opt->min_cnt = 4; + opt->max_cnt = 8; + opt->min_asm_ovlp = 33; + opt->min_merge_len = 0; + mag_init_opt(&opt->mag_opt); + opt->mag_opt.flag = MAG_F_NO_SIMPL | MAG_F_POPOPEN; +} + +void fml_opt_adjust(fml_opt_t *opt, int n_seqs, const bseq1_t *seqs) +{ + int i, log_len; + uint64_t tot_len = 0; + if (opt->n_threads < 1) opt->n_threads = 1; + for (i = 0; i < n_seqs; ++i) tot_len += seqs[i].l_seq; // compute total length + for (log_len = 10; log_len < 32; ++log_len) // compute ceil(log2(tot_len)) + if (1ULL< tot_len) break; + if (opt->ec_k == 0) opt->ec_k = (log_len + 12) / 2; + if (opt->ec_k%2 == 0) ++opt->ec_k; + opt->mag_opt.min_elen = (int)((double)tot_len / n_seqs * 2.5 + .499); +} + +static inline int is_rev_same(int l, const char *s) +{ + int i; + if (l&1) return 0; + for (i = 0; i < l>>1; ++i) + if (s[i] + s[l-1-i] != 5) break; + return (i == l>>1); +} + +struct rld_t *fml_fmi_gen(int n, bseq1_t *seq, int is_mt) +{ + mrope_t *mr; + kstring_t str = {0,0,0}; + mritr_t itr; + rlditr_t di; + const uint8_t *block; + rld_t *e = 0; + int k; + + for (k = 0; k < n; ++k) + if (seq[k].l_seq > 0) + break; + if (k == n) return 0; + + mr = mr_init(ROPE_DEF_MAX_NODES, ROPE_DEF_BLOCK_LEN, MR_SO_RCLO); + for (k = 0; k < n; ++k) { + int i; + bseq1_t *s = &seq[k]; + if (s->l_seq == 0) continue; + free(s->qual); + for (i = 0; i < s->l_seq; ++i) + s->seq[i] = seq_nt6_table[(int)s->seq[i]]; + for (i = 0; i < s->l_seq; ++i) + if (s->seq[i] == 5) break; + if (i < s->l_seq) { + free(s->seq); + continue; + } + if (is_rev_same(s->l_seq, s->seq)) + --s->l_seq, s->seq[s->l_seq] = 0; + seq_reverse(s->l_seq, (uint8_t*)s->seq); + kputsn(s->seq, s->l_seq + 1, &str); + seq_revcomp6(s->l_seq, (uint8_t*)s->seq); + kputsn(s->seq, s->l_seq + 1, &str); + free(s->seq); + } + free(seq); + mr_insert_multi(mr, str.l, (uint8_t*)str.s, is_mt); + free(str.s); + + e = rld_init(6, 3); + rld_itr_init(e, &di, 0); + mr_itr_first(mr, &itr, 1); + while ((block = mr_itr_next_block(&itr)) != 0) { + const uint8_t *q = block + 2, *end = block + 2 + *rle_nptr(block); + while (q < end) { + int c = 0; + int64_t l; + rle_dec1(q, c, l); + rld_enc(e, &di, l, c); + } + } + rld_enc_finish(e, &di); + + mr_destroy(mr); + return e; +} + +struct rld_t *fml_seq2fmi(const fml_opt_t *opt, int n, bseq1_t *seq) +{ + return fml_fmi_gen(n, seq, opt->n_threads > 1? 1 : 0); +} + +void fml_fmi_destroy(rld_t *e) +{ + rld_destroy(e); +} + +void fml_mag_clean(const fml_opt_t *opt, struct mag_t *g) +{ + magopt_t o = opt->mag_opt; + o.min_merge_len = opt->min_merge_len; + mag_g_merge(g, 1, opt->min_merge_len); + mag_g_clean(g, &o); + mag_g_trim_open(g, &o); +} + +void fml_mag_destroy(struct mag_t *g) +{ + mag_g_destroy(g); +} + +#include "khash.h" +KHASH_DECLARE(64, uint64_t, uint64_t) + +#define edge_is_del(_x) ((_x).x == (uint64_t)-2 || (_x).y == 0) // from mag.c + +fml_utg_t *fml_mag2utg(struct mag_t *g, int *n) +{ + size_t i, j; + fml_utg_t *utg; + khash_t(64) *h; + khint_t k; + + h = kh_init(64); + for (i = j = 0; i < g->v.n; ++i) { + int absent; + magv_t *p = &g->v.a[i]; + if (p->len < 0) continue; + k = kh_put(64, h, p->k[0], &absent); + kh_val(h, k) = j<<1 | 0; + k = kh_put(64, h, p->k[1], &absent); + kh_val(h, k) = j<<1 | 1; + ++j; + } + *n = j; + kh_destroy(64, g->h); + + utg = (fml_utg_t*)calloc(*n, sizeof(fml_utg_t)); + for (i = j = 0; i < g->v.n; ++i) { + magv_t *p = &g->v.a[i]; + fml_utg_t *q; + int from, a, b; + if (p->len < 0) continue; + q = &utg[j++]; + q->len = p->len, q->nsr = p->nsr; + q->seq = p->seq, q->cov = p->cov; + for (a = 0; a < q->len; ++a) + q->seq[a] = "$ACGTN"[(int)q->seq[a]]; + q->seq[q->len] = q->cov[q->len] = 0; + for (from = 0; from < 2; ++from) { + ku128_v *r = &p->nei[from]; + for (b = q->n_ovlp[from] = 0; b < r->n; ++b) + if (!edge_is_del(r->a[b])) ++q->n_ovlp[from]; + } + q->ovlp = (fml_ovlp_t*)calloc(q->n_ovlp[0] + q->n_ovlp[1], sizeof(fml_ovlp_t)); + for (from = a = 0; from < 2; ++from) { + ku128_v *r = &p->nei[from]; + for (b = 0; b < r->n; ++b) { + ku128_t *s = &r->a[b]; + fml_ovlp_t *t; + if (edge_is_del(*s)) continue; + t = &q->ovlp[a++]; + k = kh_get(64, h, s->x); + assert(k != kh_end(h)); + t->id = kh_val(h, k) >> 1; + t->to = kh_val(h, k) & 1; + t->len = s->y; + t->from = from; + } + free(p->nei[from].a); + } + } + kh_destroy(64, h); + free(g->v.a); + free(g); + return utg; +} + +void fml_utg_print(int n, const fml_utg_t *utg) +{ + int i, j, l; + kstring_t out = {0,0,0}; + for (i = 0; i < n; ++i) { + const fml_utg_t *u = &utg[i]; + out.l = 0; + kputc('@', &out); kputw(i<<1|0, &out); kputc(':', &out); kputw(i<<1|1, &out); + kputc('\t', &out); kputw(u->nsr, &out); + kputc('\t', &out); + for (j = 0; j < u->n_ovlp[0]; ++j) { + kputw(u->ovlp[j].id<<1|u->ovlp[j].to, &out); kputc(',', &out); + kputw(u->ovlp[j].len, &out); kputc(';', &out); + } + if (u->n_ovlp[0] == 0) kputc('.', &out); + kputc('\t', &out); + for (; j < u->n_ovlp[0] + u->n_ovlp[1]; ++j) { + kputw(u->ovlp[j].id<<1|u->ovlp[j].to, &out); kputc(',', &out); + kputw(u->ovlp[j].len, &out); kputc(';', &out); + } + if (u->n_ovlp[1] == 0) kputc('.', &out); + kputc('\n', &out); + l = out.l; + kputsn(u->seq, u->len, &out); + kputsn("\n+\n", 3, &out); + kputsn(u->cov, u->len, &out); + kputc('\n', &out); + fputs(out.s, stdout); + } + free(out.s); +} + +void fml_utg_print_gfa(int n, const fml_utg_t *utg) +{ + int i, j; + FILE *fp = stdout; + fputs("H\tVN:Z:1.0\n", fp); + for (i = 0; i < n; ++i) { + const fml_utg_t *u = &utg[i]; + fprintf(fp, "S\t%d\t", i); + fputs(u->seq, fp); + fprintf(fp, "\tLN:i:%d\tRC:i:%d\tPD:Z:", u->len, u->nsr); + fputs(u->cov, fp); + fputc('\n', fp); + for (j = 0; j < u->n_ovlp[0] + u->n_ovlp[1]; ++j) { + fml_ovlp_t *o = &u->ovlp[j]; + if (i < o->id) + fprintf(fp, "L\t%d\t%c\t%d\t%c\t%dM\n", i, "+-"[!o->from], o->id, "+-"[o->to], o->len); + } + } +} + +void fml_utg_destroy(int n, fml_utg_t *utg) +{ + int i; + for (i = 0; i < n; ++i) { + free(utg[i].seq); + free(utg[i].cov); + free(utg[i].ovlp); + } + free(utg); +} + +#define MAG_MIN_NSR_COEF .1 + +fml_utg_t *fml_assemble(const fml_opt_t *opt0, int n_seqs, bseq1_t *seqs, int *n_utg) +{ + rld_t *e; + mag_t *g; + fml_utg_t *utg; + fml_opt_t opt = *opt0; + float kcov; + + *n_utg = 0; + fml_opt_adjust(&opt, n_seqs, seqs); + if (opt.ec_k >= 0) fml_correct(&opt, n_seqs, seqs); + kcov = fml_fltuniq(&opt, n_seqs, seqs); + e = fml_seq2fmi(&opt, n_seqs, seqs); + if (e == 0) return 0; // this may happen when all sequences are filtered out + g = fml_fmi2mag(&opt, e); + opt.mag_opt.min_ensr = opt.mag_opt.min_ensr > kcov * MAG_MIN_NSR_COEF? opt.mag_opt.min_ensr : (int)(kcov * MAG_MIN_NSR_COEF + .499); + opt.mag_opt.min_ensr = opt.mag_opt.min_ensr < opt0->max_cnt? opt.mag_opt.min_ensr : opt0->max_cnt; + opt.mag_opt.min_ensr = opt.mag_opt.min_ensr > opt0->min_cnt? opt.mag_opt.min_ensr : opt0->min_cnt; + opt.mag_opt.min_insr = opt.mag_opt.min_ensr - 1; + fml_mag_clean(&opt, g); + utg = fml_mag2utg(g, n_utg); + return utg; +} diff --git a/mrope.c b/mrope.c new file mode 100644 index 0000000..5954f06 --- /dev/null +++ b/mrope.c @@ -0,0 +1,307 @@ +#include +#include +#include +#include +#include +#include +#include +#include "mrope.h" + +/******************************* + *** Single-string insertion *** + *******************************/ + +mrope_t *mr_init(int max_nodes, int block_len, int sorting_order) +{ + int a; + mrope_t *r; + assert(sorting_order >= 0 && sorting_order <= 2); + r = calloc(1, sizeof(mrope_t)); + r->so = sorting_order; + r->thr_min = 1000; + for (a = 0; a != 6; ++a) + r->r[a] = rope_init(max_nodes, block_len); + return r; +} + +void mr_destroy(mrope_t *r) +{ + int a; + for (a = 0; a != 6; ++a) + if (r->r[a]) rope_destroy(r->r[a]); + free(r); +} + +int mr_thr_min(mrope_t *r, int thr_min) +{ + if (thr_min > 0) + r->thr_min = thr_min; + return r->thr_min; +} + +int64_t mr_insert1(mrope_t *r, const uint8_t *str) +{ + int64_t tl[6], tu[6], l, u; + const uint8_t *p; + int b, is_srt = (r->so != MR_SO_IO), is_comp = (r->so == MR_SO_RCLO); + for (u = 0, b = 0; b != 6; ++b) u += r->r[b]->c[0]; + l = is_srt? 0 : u; + for (p = str, b = 0; *p; b = *p++) { + int a; + if (l != u) { + int64_t cnt = 0; + rope_rank2a(r->r[b], l, u, tl, tu); + if (is_comp && *p != 5) { + for (a = 4; a > *p; --a) l += tu[a] - tl[a]; + l += tu[0] - tl[0]; + } else for (a = 0; a < *p; ++a) l += tu[a] - tl[a]; + rope_insert_run(r->r[b], l, *p, 1, 0); + while (--b >= 0) cnt += r->r[b]->c[*p]; + l = cnt + tl[*p]; u = cnt + tu[*p]; + } else { + l = rope_insert_run(r->r[b], l, *p, 1, 0); + while (--b >= 0) l += r->r[b]->c[*p]; + u = l; + } + } + return rope_insert_run(r->r[b], l, 0, 1, 0); +} + +void mr_rank2a(const mrope_t *mr, int64_t x, int64_t y, int64_t *cx, int64_t *cy) +{ + int a, b; + int64_t z, c[6], l; + memset(c, 0, 48); + for (a = 0, z = 0; a < 6; ++a) { + const int64_t *ca = mr->r[a]->c; + l = ca[0] + ca[1] + ca[2] + ca[3] + ca[4] + ca[5]; + if (z + l >= x) break; + for (b = 0; b < 6; ++b) c[b] += ca[b]; + z += l; + } + assert(a != 6); + if (y >= 0 && z + l >= y) { // [x,y) is in the same bucket + rope_rank2a(mr->r[a], x - z, y - z, cx, cy); + for (b = 0; b < 6; ++b) + cx[b] += c[b], cy[b] += c[b]; + return; + } + if (x != z) rope_rank1a(mr->r[a], x - z, cx); + else memset(cx, 0, 48); + for (b = 0; b < 6; ++b) + cx[b] += c[b], c[b] += mr->r[a]->c[b]; + if (y < 0) return; + for (++a, z += l; a < 6; ++a) { + const int64_t *ca = mr->r[a]->c; + l = ca[0] + ca[1] + ca[2] + ca[3] + ca[4] + ca[5]; + if (z + l >= y) break; + for (b = 0; b < 6; ++b) c[b] += ca[b]; + z += l; + } + assert(a != 6); + if (y != z + l) rope_rank1a(mr->r[a], y - z, cy); + else for (b = 0; b < 6; ++b) cy[b] = mr->r[a]->c[b]; + for (b = 0; b < 6; ++b) cy[b] += c[b]; +} + +/********************** + *** Mrope iterator *** + **********************/ + +void mr_itr_first(mrope_t *r, mritr_t *i, int to_free) +{ + i->a = 0; i->r = r; i->to_free = to_free; + rope_itr_first(i->r->r[0], &i->i); +} + +const uint8_t *mr_itr_next_block(mritr_t *i) +{ + const uint8_t *s; + if (i->a >= 6) return 0; + while ((s = rope_itr_next_block(&i->i)) == 0) { + if (i->to_free) { + rope_destroy(i->r->r[i->a]); + i->r->r[i->a] = 0; + } + if (++i->a == 6) return 0; + rope_itr_first(i->r->r[i->a], &i->i); + } + return i->a == 6? 0 : s; +} + +/***************************************** + *** Inserting multiple strings in RLO *** + *****************************************/ + +typedef struct { + uint64_t l; + uint64_t u:61, c:3; + const uint8_t *p; +} triple64_t; + +typedef const uint8_t *cstr_t; + +#define rope_comp6(c) ((c) >= 1 && (c) <= 4? 5 - (c) : (c)) + +static void mr_insert_multi_aux(rope_t *rope, int64_t m, triple64_t *a, int is_comp) +{ + int64_t k, beg; + rpcache_t cache; + memset(&cache, 0, sizeof(rpcache_t)); + for (k = 0; k != m; ++k) // set the base to insert + a[k].c = *a[k].p++; + for (k = 1, beg = 0; k <= m; ++k) { + if (k == m || a[k].u != a[k-1].u) { + int64_t x, i, l = a[beg].l, u = a[beg].u, tl[6], tu[6], c[6]; + int start, end, step, b; + if (l == u && k == beg + 1) { // special case; still works without the following block + a[beg].l = a[beg].u = rope_insert_run(rope, l, a[beg].c, 1, &cache); + beg = k; + continue; + } else if (l == u) { + memset(tl, 0, 48); + memset(tu, 0, 48); + } else rope_rank2a(rope, l, u, tl, tu); + memset(c, 0, 48); + for (i = beg; i < k; ++i) ++c[a[i].c]; + // insert sentinel + if (c[0]) rope_insert_run(rope, l, 0, c[0], &cache); + // insert A/C/G/T + x = l + c[0] + (tu[0] - tl[0]); + if (is_comp) start = 4, end = 0, step = -1; + else start = 1, end = 5, step = 1; + for (b = start; b != end; b += step) { + int64_t size = tu[b] - tl[b]; + if (c[b]) { + tl[b] = rope_insert_run(rope, x, b, c[b], &cache); + tu[b] = tl[b] + size; + } + x += c[b] + size; + } + // insert N + if (c[5]) { + tu[5] -= tl[5]; + tl[5] = rope_insert_run(rope, x, 5, c[5], &cache); + tu[5] += tl[5]; + } + // update a[] + for (i = beg; i < k; ++i) { + triple64_t *p = &a[i]; + p->l = tl[p->c], p->u = tu[p->c]; + } + beg = k; + } + } +} + +typedef struct { + volatile int *n_fin_workers; + volatile int to_run; + int to_exit; + mrope_t *mr; + int b, is_comp; + int64_t m; + triple64_t *a; +} worker_t; + +static void *worker(void *data) +{ + worker_t *w = (worker_t*)data; + struct timespec req, rem; + req.tv_sec = 0; req.tv_nsec = 1000000; + do { + while (!__sync_bool_compare_and_swap(&w->to_run, 1, 0)) nanosleep(&req, &rem); // wait for the signal from the master thread + if (w->m) mr_insert_multi_aux(w->mr->r[w->b], w->m, w->a, w->is_comp); + __sync_add_and_fetch(w->n_fin_workers, 1); + } while (!w->to_exit); + return 0; +} + +void mr_insert_multi(mrope_t *mr, int64_t len, const uint8_t *s, int is_thr) +{ + int64_t k, m, n0; + int b, is_srt = (mr->so != MR_SO_IO), is_comp = (mr->so == MR_SO_RCLO), stop_thr = 0; + volatile int n_fin_workers = 0; + triple64_t *a[2], *curr, *prev, *swap; + pthread_t *tid = 0; + worker_t *w = 0; + + if (mr->thr_min < 0) mr->thr_min = 0; + assert(len > 0 && s[len-1] == 0); + { // split into short strings + cstr_t p, q, end = s + len; + for (p = s, m = 0; p != end; ++p) // count #sentinels + if (*p == 0) ++m; + curr = a[0] = malloc(m * sizeof(triple64_t)); + prev = a[1] = malloc(m * sizeof(triple64_t)); + for (p = q = s, k = 0; p != end; ++p) // find the start of each string + if (*p == 0) prev[k++].p = q, q = p + 1; + } + + for (k = n0 = 0; k < 6; ++k) n0 += mr->r[k]->c[0]; + for (k = 0; k != m; ++k) { + if (is_srt) prev[k].l = 0, prev[k].u = n0; + else prev[k].l = prev[k].u = n0 + k; + prev[k].c = 0; + } + mr_insert_multi_aux(mr->r[0], m, prev, is_comp); // insert the first (actually the last) column + + if (is_thr) { + tid = alloca(4 * sizeof(pthread_t)); + w = alloca(4 * sizeof(worker_t)); + memset(w, 0, 4 * sizeof(worker_t)); + for (b = 0; b < 4; ++b) { + w[b].mr = mr, w[b].b = b + 1, w[b].is_comp = is_comp; + w[b].n_fin_workers = &n_fin_workers; + } + for (b = 0; b < 4; ++b) pthread_create(&tid[b], 0, worker, &w[b]); + } + + n0 = 0; // the number of inserted strings + while (m) { + int64_t c[6], ac[6]; + triple64_t *q[6]; + + memset(c, 0, 48); + for (k = n0; k != m; ++k) ++c[prev[k].c]; // counting + for (q[0] = curr + n0, b = 1; b < 6; ++b) q[b] = q[b-1] + c[b-1]; + if (n0 + c[0] < m) { + for (k = n0; k != m; ++k) *q[prev[k].c]++ = prev[k]; // sort + for (b = 0; b < 6; ++b) q[b] -= c[b]; + } + n0 += c[0]; + + if (is_thr && !stop_thr) { + struct timespec req, rem; + req.tv_sec = 0; req.tv_nsec = 1000000; + stop_thr = (m - n0 <= mr->thr_min); + for (b = 0; b < 4; ++b) { + w[b].a = q[b+1], w[b].m = c[b+1]; + if (stop_thr) w[b].to_exit = 1; // signal the workers to exit + while (!__sync_bool_compare_and_swap(&w[b].to_run, 0, 1)); // signal the workers to start + } + if (c[5]) mr_insert_multi_aux(mr->r[5], c[5], q[5], is_comp); // the master thread processes the "N" bucket + while (!__sync_bool_compare_and_swap(&n_fin_workers, 4, 0)) // wait until all 4 workers finish + nanosleep(&req, &rem); + if (stop_thr && n0 < m) + fprintf(stderr, "[M::%s] Turn off parallelization for this batch as too few strings are left.\n", __func__); + } else { + for (b = 1; b < 6; ++b) + if (c[b]) mr_insert_multi_aux(mr->r[b], c[b], q[b], is_comp); + } + if (n0 == m) break; + + memset(ac, 0, 48); + for (b = 1; b < 6; ++b) { // update the intervals to account for buckets ahead + int a; + for (a = 0; a < 6; ++a) ac[a] += mr->r[b-1]->c[a]; + for (k = 0; k < c[b]; ++k) { + triple64_t *p = &q[b][k]; + p->l += ac[p->c]; p->u += ac[p->c]; + } + } + swap = curr, curr = prev, prev = swap; + } + if (is_thr) for (b = 0; b < 4; ++b) pthread_join(tid[b], 0); + free(a[0]); free(a[1]); +} diff --git a/mrope.h b/mrope.h new file mode 100644 index 0000000..240448c --- /dev/null +++ b/mrope.h @@ -0,0 +1,114 @@ +#ifndef MROPE_H_ +#define MROPE_H_ + +#include "rope.h" + +#define MR_SO_IO 0 +#define MR_SO_RLO 1 +#define MR_SO_RCLO 2 + +typedef struct { + uint8_t so; // sorting order + int thr_min; // when there are fewer sequences than this, disable multi-threading + rope_t *r[6]; +} mrope_t; // multi-rope + +typedef struct { + mrope_t *r; + int a, to_free; + rpitr_t i; +} mritr_t; + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * Initiate a multi-rope + * + * @param max_nodes maximum number of nodes in an internal node; use ROPE_DEF_MAX_NODES (64) if unsure + * @param block_len maximum block length in an external node; use ROPE_DEF_BLOCK_LEN (256) if unsure + * @param sorting_order the order in which sequences are added; possible values defined by the MR_SO_* macros + */ + mrope_t *mr_init(int max_nodes, int block_len, int sorting_order); + + void mr_destroy(mrope_t *r); + + int mr_thr_min(mrope_t *r, int thr_min); + + /** + * Insert one string into the index + * + * @param r multi-rope + * @param str the *reverse* of the input string (important: it is reversed!) + */ + int64_t mr_insert1(mrope_t *r, const uint8_t *str); + + /** + * Insert multiple strings + * + * @param mr multi-rope + * @param len total length of $s + * @param s concatenated, NULL delimited, reversed input strings + * @param is_thr true to use 5 threads + */ + void mr_insert_multi(mrope_t *mr, int64_t len, const uint8_t *s, int is_thr); + + void mr_rank2a(const mrope_t *mr, int64_t x, int64_t y, int64_t *cx, int64_t *cy); + #define mr_rank1a(mr, x, cx) mr_rank2a(mr, x, -1, cx, 0) + + /** + * Put the iterator at the start of the index + * + * @param r multi-rope + * @param i iterator to be initialized + * @param to_free if true, free visited buckets + */ + void mr_itr_first(mrope_t *r, mritr_t *i, int to_free); + + /** + * Iterate to the next block + * + * @param i iterator + * + * @return pointer to the start of a block; see rle.h for decoding the block + */ + const uint8_t *mr_itr_next_block(mritr_t *i); + +#ifdef __cplusplus +} +#endif + +static inline int64_t mr_get_c(const mrope_t *mr, int64_t c[6]) +{ + int a, b; + int64_t tot = 0; + for (a = 0; a < 6; ++a) c[a] = 0; + for (a = 0; a < 6; ++a) { + for (b = 0; b < 6; ++b) + c[b] += mr->r[a]->c[b]; + tot += c[b]; + } + return tot; +} + +static inline int64_t mr_get_ac(const mrope_t *mr, int64_t ac[7]) +{ + int a; + int64_t c[6], tot; + tot = mr_get_c(mr, c); + for (a = 1, ac[0] = 0; a <= 6; ++a) ac[a] = ac[a-1] + c[a-1]; + return tot; +} + +static inline int64_t mr_get_tot(const mrope_t *mr) +{ + int a, b; + int64_t tot = 0; + for (a = 0; a < 6; ++a) + for (b = 0; b < 6; ++b) + tot += mr->r[a]->c[b]; + return tot; +} + +#endif diff --git a/rld0.c b/rld0.c new file mode 100644 index 0000000..7f8b5ba --- /dev/null +++ b/rld0.c @@ -0,0 +1,489 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "rld0.h" + +#define RLD_IBITS_PLUS 4 + +#define rld_file_size(e) ((4 + (e)->asize) * 8 + (e)->n_bytes + 8 * (e)->n_frames * ((e)->asize + 1)) + +#ifndef xcalloc +#define xcalloc(n, s) calloc(n, s) +#endif +#ifndef xmalloc +#define xmalloc(s) malloc(s) +#endif + +/****************** + * Delta encoding * + ******************/ + +static const char LogTable256[256] = { +#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n + -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), + LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) +}; + +static inline int ilog2_32(uint32_t v) +{ + register uint32_t t, tt; + if ((tt = v>>16)) return (t = tt>>8) ? 24 + LogTable256[t] : 16 + LogTable256[tt]; + return (t = v>>8) ? 8 + LogTable256[t] : LogTable256[v]; +} + +static inline int ilog2(uint64_t v) +{ + return v>>32? 32 + ilog2_32(v>>32) : ilog2_32(v); +} + +static inline int64_t rld_delta_enc1(uint64_t x, int *width) +{ + int y = ilog2(x); + int z = ilog2_32(y + 1); + *width = (z<<1) + 1 + y; + return (x ^ (uint64_t)1<n = 1; + e->z = xmalloc(sizeof(void*)); + e->z[0] = xcalloc(RLD_LSIZE, 8); + e->ssize = 1<cnt = xcalloc(asize + 1, 8); + e->mcnt = xcalloc(asize + 1, 8); + e->abits = ilog2(asize) + 1; + e->asize = asize; + e->sbits = bbits; + e->asize1 = asize + 1; + e->offset0[0] = (e->asize1*16+63)/64; + e->offset0[1] = (e->asize1*32+63)/64; + e->offset0[2] = e->asize1; + return e; +} + +void rld_destroy(rld_t *e) +{ + int i = 0; + if (e == 0) return; + if (e->mem) { + close(e->fd); + munmap(e->mem, rld_file_size(e)); + } else { + for (i = 0; i < e->n; ++i) free(e->z[i]); + free(e->frame); + } + free(e->z); free(e->cnt); free(e->mcnt); free(e); +} + +void rld_itr_init(const rld_t *e, rlditr_t *itr, uint64_t k) +{ + itr->i = e->z + (k >> RLD_LBITS); + itr->shead = *itr->i + k%RLD_LSIZE; + itr->stail = rld_get_stail(e, itr); + itr->p = itr->shead + e->offset0[rld_block_type(*itr->shead)]; + itr->q = (uint8_t*)itr->p; + itr->r = 64; + itr->c = -1; + itr->l = 0; +} + +/************ + * Encoding * + ************/ + +static inline void enc_next_block(rld_t *e, rlditr_t *itr) +{ + int i, type; + if (itr->stail + 2 - *itr->i == RLD_LSIZE) { + ++e->n; + e->z = realloc(e->z, e->n * sizeof(void*)); + itr->i = e->z + e->n - 1; + itr->shead = *itr->i = xcalloc(RLD_LSIZE, 8); + } else itr->shead += e->ssize; + if (e->cnt[0] - e->mcnt[0] < 0x4000) { + uint16_t *p = (uint16_t*)itr->shead; + for (i = 0; i <= e->asize; ++i) p[i] = e->cnt[i] - e->mcnt[i]; + type = 0; + } else if (e->cnt[0] - e->mcnt[0] < 0x40000000) { + uint32_t *p = (uint32_t*)itr->shead; + for (i = 0; i <= e->asize; ++i) p[i] = e->cnt[i] - e->mcnt[i]; + type = 1; + } else { + uint64_t *p = (uint64_t*)itr->shead; + for (i = 0; i <= e->asize; ++i) p[i] = e->cnt[i] - e->mcnt[i]; + type = 2; + } + *itr->shead |= (uint64_t)type<<62; + itr->p = itr->shead + e->offset0[type]; + itr->stail = rld_get_stail(e, itr); + itr->q = (uint8_t*)itr->p; + itr->r = 64; + for (i = 0; i <= e->asize; ++i) e->mcnt[i] = e->cnt[i]; +} + +static int rld_enc1(rld_t *e, rlditr_t *itr, int64_t l, uint8_t c) +{ + int w; + uint64_t x = rld_delta_enc1(l, &w) << e->abits | c; + w += e->abits; + if (w >= itr->r && itr->p == itr->stail) enc_next_block(e, itr); + if (w > itr->r) { + w -= itr->r; + *itr->p++ |= x >> w; + *itr->p = x << (itr->r = 64 - w); + } else itr->r -= w, *itr->p |= x << itr->r; + e->cnt[0] += l; + e->cnt[c + 1] += l; + return 0; +} + +int rld_enc(rld_t *e, rlditr_t *itr, int64_t l, uint8_t c) +{ + if (l == 0) return 0; + if (itr->c != c) { + if (itr->l) rld_enc1(e, itr, itr->l, itr->c); + itr->l = l; itr->c = c; + } else itr->l += l; + return 0; +} + +void rld_rank_index(rld_t *e) +{ + uint64_t last, n_blks, i, k, *cnt; + int j; + + n_blks = e->n_bytes * 8 / 64 / e->ssize + 1; + last = rld_last_blk(e); + cnt = alloca(e->asize * 8); + e->ibits = ilog2(e->mcnt[0] / n_blks) + RLD_IBITS_PLUS; + e->n_frames = ((e->mcnt[0] + (1ll<ibits) - 1) >> e->ibits) + 1; + e->frame = xcalloc(e->n_frames * e->asize1, 8); + e->frame[0] = 0; + for (j = 0; j < e->asize; ++j) cnt[j] = 0; + for (i = e->ssize, k = 1; i <= last; i += e->ssize) { + uint64_t sum, *p = rld_seek_blk(e, i); + int type = rld_block_type(*p); + if (type == 0) { + uint16_t *q = (uint16_t*)p; + for (j = 1; j <= e->asize; ++j) cnt[j-1] += q[j]; + } else if (type == 1) { + uint32_t *q = (uint32_t*)p; + for (j = 1; j <= e->asize; ++j) cnt[j-1] += q[j] & 0x3fffffff; + } else { + uint64_t *q = (uint64_t*)p; + for (j = 1; j <= e->asize; ++j) cnt[j-1] += q[j]; + } + for (j = 0, sum = 0; j < e->asize; ++j) sum += cnt[j]; + while (sum >= k<ibits) ++k; + if (k < e->n_frames) { + uint64_t x = k * e->asize1; + e->frame[x] = i; + for (j = 0; j < e->asize; ++j) e->frame[x + j + 1] = cnt[j]; + } + } + assert(k >= e->n_frames - 1); + for (k = 1; k < e->n_frames; ++k) { // fill zero cells + uint64_t x = k * e->asize1; + if (e->frame[x] == 0) { + for (j = 0; j <= e->asize; ++j) + e->frame[x + j] = e->frame[x - e->asize1 + j]; + } + } +} + +uint64_t rld_enc_finish(rld_t *e, rlditr_t *itr) +{ + int i; + if (itr->l) rld_enc1(e, itr, itr->l, itr->c); + enc_next_block(e, itr); + e->n_bytes = (((uint64_t)(e->n - 1) * RLD_LSIZE) + (itr->p - *itr->i)) * 8; + // recompute e->cnt as the accumulative count; e->mcnt[] keeps the marginal counts + for (e->cnt[0] = 0, i = 1; i <= e->asize; ++i) e->cnt[i] += e->cnt[i - 1]; + rld_rank_index(e); + return e->n_bytes; +} + +/***************** + * Save and load * + *****************/ + +int rld_dump(const rld_t *e, const char *fn) +{ + uint64_t k = 0; + int i; + uint32_t a; + FILE *fp; + fp = strcmp(fn, "-")? fopen(fn, "wb") : fdopen(fileno(stdout), "wb"); + if (fp == 0) return -1; + a = e->asize<<16 | e->sbits; + fwrite("RLD\3", 1, 4, fp); // write magic + fwrite(&a, 4, 1, fp); // write sbits and asize + fwrite(&k, 8, 1, fp); // preserve 8 bytes for future uses + fwrite(&e->n_bytes, 8, 1, fp); // n_bytes can always be divided by 8 + fwrite(&e->n_frames, 8, 1, fp); // number of frames + fwrite(e->mcnt + 1, 8, e->asize, fp); // write the marginal counts + for (i = 0, k = e->n_bytes / 8; i < e->n - 1; ++i, k -= RLD_LSIZE) + fwrite(e->z[i], 8, RLD_LSIZE, fp); + fwrite(e->z[i], 8, k, fp); + fwrite(e->frame, 8 * e->asize1, e->n_frames, fp); + fclose(fp); + return 0; +} + +static rld_t *rld_restore_header(const char *fn, FILE **_fp) +{ + FILE *fp; + rld_t *e; + char magic[4]; + uint64_t a[3]; + int32_t i, x; + + if (strcmp(fn, "-") == 0) *_fp = fp = stdin; + else if ((*_fp = fp = fopen(fn, "rb")) == 0) return 0; + fread(magic, 1, 4, fp); + if (strncmp(magic, "RLD\3", 4)) return 0; + fread(&x, 4, 1, fp); + e = rld_init(x>>16, x&0xffff); + fread(a, 8, 3, fp); + e->n_bytes = a[1]; e->n_frames = a[2]; + fread(e->mcnt + 1, 8, e->asize, fp); + for (i = 0; i <= e->asize; ++i) e->cnt[i] = e->mcnt[i]; + for (i = 1; i <= e->asize; ++i) e->cnt[i] += e->cnt[i - 1]; + e->mcnt[0] = e->cnt[e->asize]; + return e; +} + +rld_t *rld_restore(const char *fn) +{ + FILE *fp; + rld_t *e; + uint64_t k, n_blks; + int32_t i; + + if ((e = rld_restore_header(fn, &fp)) == 0) { // then load as plain DNA rle + uint8_t *buf; + int l; + rlditr_t itr; + buf = malloc(0x10000); + e = rld_init(6, 3); + rld_itr_init(e, &itr, 0); + while ((l = fread(buf, 1, 0x10000, fp)) != 0) + for (i = 0; i < l; ++i) + if (buf[i]>>3) rld_enc(e, &itr, buf[i]>>3, buf[i]&7); + fclose(fp); + free(buf); + rld_enc_finish(e, &itr); + return e; + } + if (e->n_bytes / 8 > RLD_LSIZE) { // allocate enough memory + e->n = (e->n_bytes / 8 + RLD_LSIZE - 1) / RLD_LSIZE; + e->z = realloc(e->z, e->n * sizeof(void*)); + for (i = 1; i < e->n; ++i) + e->z[i] = xcalloc(RLD_LSIZE, 8); + } + for (i = 0, k = e->n_bytes / 8; i < e->n - 1; ++i, k -= RLD_LSIZE) + fread(e->z[i], 8, RLD_LSIZE, fp); + fread(e->z[i], 8, k, fp); + e->frame = xmalloc(e->n_frames * e->asize1 * 8); + fread(e->frame, 8 * e->asize1, e->n_frames, fp); + fclose(fp); + n_blks = e->n_bytes * 8 / 64 / e->ssize + 1; + e->ibits = ilog2(e->mcnt[0] / n_blks) + RLD_IBITS_PLUS; + return e; +} + +rld_t *rld_restore_mmap(const char *fn) +{ + FILE *fp; + rld_t *e; + int i; + int64_t n_blks; + + e = rld_restore_header(fn, &fp); + fclose(fp); + free(e->z[0]); free(e->z); + e->n = (e->n_bytes / 8 + RLD_LSIZE - 1) / RLD_LSIZE; + e->z = xcalloc(e->n, sizeof(void*)); + e->fd = open(fn, O_RDONLY); + e->mem = (uint64_t*)mmap(0, rld_file_size(e), PROT_READ, MAP_PRIVATE, e->fd, 0); + for (i = 0; i < e->n; ++i) e->z[i] = e->mem + (4 + e->asize) + (size_t)i * RLD_LSIZE; + e->frame = e->mem + (4 + e->asize) + e->n_bytes/8; + n_blks = e->n_bytes * 8 / 64 / e->ssize + 1; + e->ibits = ilog2(e->mcnt[0] / n_blks) + RLD_IBITS_PLUS; + return e; +} + +/****************** + * Computing rank * + ******************/ + +#ifdef _DNA_ONLY +static inline int64_t rld_dec0_fast_dna(const rld_t *e, rlditr_t *itr, int *c) +{ // This is NOT a replacement of rld_dec0(). It does not do boundary check. + uint64_t x = itr->r == 64? itr->p[0] : itr->p[0] << (64 - itr->r) | itr->p[1] >> itr->r; + if (x>>63 == 0) { + int64_t y; + int l, w = 0x333333335555779bll>>(x>>59<<2)&0xf; + l = (x >> (64 - w)) - 1; + y = x << w >> (64 - l) | 1u << l; + w += l; + *c = x << w >> 61; + w += 3; + itr->r -= w; + if (itr->r <= 0) ++itr->p, itr->r += 64; + return y; + } else { + *c = x << 1 >> 61; + itr->r -= 4; + if (itr->r <= 0) ++itr->p, itr->r += 64; + return 1; + } +} +#endif + +static inline uint64_t rld_locate_blk(const rld_t *e, rlditr_t *itr, uint64_t k, uint64_t *cnt, uint64_t *sum) +{ + int j; + uint64_t c = 0, *q, *z = e->frame + (k>>e->ibits) * e->asize1; + itr->i = e->z + (*z>>RLD_LBITS); + q = itr->p = *itr->i + (*z&RLD_LMASK); + for (j = 1, *sum = 0; j < e->asize1; ++j) *sum += (cnt[j-1] = z[j]); + while (1) { // seek to the small block + int type; + q += e->ssize; + if (q - *itr->i == RLD_LSIZE) q = *++itr->i; + type = rld_block_type(*q); + c = type == 2? *q&0x3fffffffffffffffULL : type == 1? *(uint32_t*)q : *(uint16_t*)q; + if (*sum + c > k) break; + if (type == 0) { + uint16_t *p = (uint16_t*)q + 1; +#ifdef _DNA_ONLY + cnt[0] += p[0]; cnt[1] += p[1]; cnt[2] += p[2]; cnt[3] += p[3]; cnt[4] += p[4]; cnt[5] += p[5]; +#else + for (j = 0; j < e->asize; ++j) cnt[j] += p[j]; +#endif + } else if (type == 1) { + uint32_t *p = (uint32_t*)q + 1; + for (j = 0; j < e->asize; ++j) cnt[j] += p[j] & 0x3fffffff; + } else { + uint64_t *p = (uint64_t*)q + 1; + for (j = 0; j < e->asize; ++j) cnt[j] += p[j]; + } + *sum += c; + itr->p = q; + } + itr->shead = itr->p; + itr->stail = rld_get_stail(e, itr); + itr->p += e->offset0[rld_block_type(*itr->shead)]; + itr->q = (uint8_t*)itr->p; + itr->r = 64; + return c + *sum; +} + +void rld_rank21(const rld_t *e, uint64_t k, uint64_t l, int c, uint64_t *ok, uint64_t *ol) // FIXME: can be faster +{ + *ok = rld_rank11(e, k, c); + *ol = rld_rank11(e, l, c); +} + +int rld_rank1a(const rld_t *e, uint64_t k, uint64_t *ok) +{ + uint64_t z, l; + int a = -1; + rlditr_t itr; + if (k == 0) { + for (a = 0; a < e->asize; ++a) ok[a] = 0; + return -1; + } + rld_locate_blk(e, &itr, k-1, ok, &z); + while (1) { +#ifdef _DNA_ONLY + l = rld_dec0_fast_dna(e, &itr, &a); +#else + l = rld_dec0(e, &itr, &a); +#endif + if (z + l >= k) break; + z += l; ok[a] += l; + } + ok[a] += k - z; + return a; +} + +uint64_t rld_rank11(const rld_t *e, uint64_t k, int c) +{ + uint64_t *ok; + if (k == (uint64_t)-1) return 0; + ok = alloca(e->asize1 * 8); + rld_rank1a(e, k, ok); + return ok[c]; +} + +void rld_rank2a(const rld_t *e, uint64_t k, uint64_t l, uint64_t *ok, uint64_t *ol) +{ + uint64_t z, y, len; + rlditr_t itr; + int a = -1; + if (k == 0) { + for (a = 0; a < e->asize; ++a) ok[a] = 0; + rld_rank1a(e, l, ol); + return; + } + y = rld_locate_blk(e, &itr, k-1, ok, &z); // locate the block bracketing k + while (1) { // compute ok[] +#ifdef _DNA_ONLY + len = rld_dec0_fast_dna(e, &itr, &a); +#else + len = rld_dec0(e, &itr, &a); +#endif + if (z + len >= k) break; + z += len; ok[a] += len; + } + if (y > l) { // we do not need to decode other blocks + int b; + for (b = 0; b < e->asize; ++b) ol[b] = ok[b]; // copy ok[] to ol[] + ok[a] += k - z; // finalize ok[a] + if (z + len < l) { // we need to decode the next run + z += len; ol[a] += len; + while (1) { + len = rld_dec0(e, &itr, &a); + if (z + len >= l) break; + z += len; ol[a] += len; + } + } + ol[a] += l - z; + } else { // we have to decode other blocks + ok[a] += k - z; + rld_rank1a(e, l, ol); + } +} + +int rld_extend(const rld_t *e, const rldintv_t *ik, rldintv_t ok[6], int is_back) +{ // TODO: this can be accelerated a little by using rld_rank1a() when ik.x[2]==1 + uint64_t tk[6], tl[6]; + int i; + rld_rank2a(e, ik->x[!is_back], ik->x[!is_back] + ik->x[2], tk, tl); + for (i = 0; i < 6; ++i) { + ok[i].x[!is_back] = e->cnt[i] + tk[i]; + ok[i].x[2] = (tl[i] -= tk[i]); + } + ok[0].x[is_back] = ik->x[is_back]; + ok[4].x[is_back] = ok[0].x[is_back] + tl[0]; + ok[3].x[is_back] = ok[4].x[is_back] + tl[4]; + ok[2].x[is_back] = ok[3].x[is_back] + tl[3]; + ok[1].x[is_back] = ok[2].x[is_back] + tl[2]; + ok[5].x[is_back] = ok[1].x[is_back] + tl[1]; + return 0; +} diff --git a/rld0.h b/rld0.h new file mode 100644 index 0000000..c811cad --- /dev/null +++ b/rld0.h @@ -0,0 +1,137 @@ +#ifndef RLDELTA0_H +#define RLDELTA0_H + +#define _DNA_ONLY + +#include +#include +#include +#include + +#define RLD_LBITS 23 +#define RLD_LSIZE (1<n_bytes>>3>>(e)->sbits<<(e)->sbits) +#define rld_seek_blk(e, k) ((e)->z[(k)>>RLD_LBITS] + ((k)&RLD_LMASK)) +#define rld_get_stail(e, itr) ((itr)->shead + (e)->ssize - ((itr)->shead + (e)->ssize - *(itr)->i == RLD_LSIZE? 2 : 1)) + +#define rld_block_type(x) ((uint64_t)(x)>>62) + +static inline int64_t rld_dec0(const rld_t *e, rlditr_t *itr, int *c) +{ + int w; + uint64_t x; + int64_t l, y = 0; + x = itr->p[0] << (64 - itr->r) | (itr->p != itr->stail && itr->r != 64? itr->p[1] >> itr->r : 0); + if (x>>63 == 0) { + if ((w = 0x333333335555779bll>>(x>>59<<2)&0xf) == 0xb && x>>58 == 0) return 0; + l = (x >> (64 - w)) - 1; + y = x << w >> (64 - l) | 1u << l; + w += l; + } else w = y = 1; + *c = x << w >> (64 - e->abits); + w += e->abits; + if (itr->r > w) itr->r -= w; + else ++itr->p, itr->r = 64 + itr->r - w; + return y; +} + +static inline int64_t rld_dec(const rld_t *e, rlditr_t *itr, int *_c, int is_free) +{ + int64_t l = rld_dec0(e, itr, _c); + if (l == 0 || *_c > e->asize) { + uint64_t last = rld_last_blk(e); + if (itr->p - *itr->i > RLD_LSIZE - e->ssize) { + if (is_free) { + free(*itr->i); *itr->i = 0; + } + itr->shead = *++itr->i; + } else itr->shead += e->ssize; + if (itr->shead == rld_seek_blk(e, last)) return -1; + itr->p = itr->shead + e->offset0[rld_block_type(*itr->shead)]; + itr->q = (uint8_t*)itr->p; + itr->stail = rld_get_stail(e, itr); + itr->r = 64; + return rld_dec0(e, itr, _c); + } else return l; +} + +// take k symbols from e0 and write it to e +static inline void rld_dec_enc(rld_t *e, rlditr_t *itr, const rld_t *e0, rlditr_t *itr0, int64_t k) +{ + if (itr0->l >= k) { // there are more pending symbols + rld_enc(e, itr, k, itr0->c); + itr0->l -= k; // l - k symbols remains + } else { // use up all pending symbols + int c = -1; // to please gcc + int64_t l; + rld_enc(e, itr, itr0->l, itr0->c); // write all pending symbols + k -= itr0->l; + for (; k > 0; k -= l) { // we always go into this loop because l0l = -k; itr0->c = c; + } +} + +#endif diff --git a/rle.c b/rle.c new file mode 100644 index 0000000..221e1cd --- /dev/null +++ b/rle.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include "rle.h" + +const uint8_t rle_auxtab[8] = { 0x01, 0x11, 0x21, 0x31, 0x03, 0x13, 0x07, 0x17 }; + +// insert symbol $a after $x symbols in $str; marginal counts added to $cnt; returns the size increase +int rle_insert_cached(uint8_t *block, int64_t x, int a, int64_t rl, int64_t cnt[6], const int64_t ec[6], int *beg, int64_t bc[6]) +{ + uint16_t *nptr = (uint16_t*)block; + int diff; + + block += 2; // skip the first 2 counting bytes + if (*nptr == 0) { + memset(cnt, 0, 48); + diff = rle_enc1(block, a, rl); + } else { + uint8_t *p, *end = block + *nptr, *q; + int64_t pre, z, l = 0, tot, beg_l; + int c = -1, n_bytes = 0, n_bytes2, t = 0; + uint8_t tmp[24]; + beg_l = bc[0] + bc[1] + bc[2] + bc[3] + bc[4] + bc[5]; + tot = ec[0] + ec[1] + ec[2] + ec[3] + ec[4] + ec[5]; + if (x < beg_l) { + beg_l = 0, *beg = 0; + memset(bc, 0, 48); + } + if (x == beg_l) { + p = q = block + (*beg); z = beg_l; + memcpy(cnt, bc, 48); + } else if (x - beg_l <= ((tot-beg_l)>>1) + ((tot-beg_l)>>3)) { // forward + z = beg_l; p = block + (*beg); + memcpy(cnt, bc, 48); + while (z < x) { + rle_dec1(p, c, l); + z += l; cnt[c] += l; + } + for (q = p - 1; *q>>6 == 2; --q); + } else { // backward + memcpy(cnt, ec, 48); + z = tot; p = end; + while (z >= x) { + --p; + if (*p>>6 != 2) { + l |= *p>>7? (int64_t)rle_auxtab[*p>>3&7]>>4 << t : *p>>3; + z -= l; cnt[*p&7] -= l; + l = 0; t = 0; + } else { + l |= (*p&0x3fL) << t; + t += 6; + } + } + q = p; + rle_dec1(p, c, l); + z += l; cnt[c] += l; + } + *beg = q - block; + memcpy(bc, cnt, 48); + bc[c] -= l; + n_bytes = p - q; + if (x == z && a != c && p < end) { // then try the next run + int tc; + int64_t tl; + q = p; + rle_dec1(q, tc, tl); + if (a == tc) + c = tc, n_bytes = q - p, l = tl, z += l, p = q, cnt[tc] += tl; + } + if (z != x) cnt[c] -= z - x; + pre = x - (z - l); p -= n_bytes; + if (a == c) { // insert to the same run + n_bytes2 = rle_enc1(tmp, c, l + rl); + } else if (x == z) { // at the end; append to the existing run + p += n_bytes; n_bytes = 0; + n_bytes2 = rle_enc1(tmp, a, rl); + } else { // break the current run + n_bytes2 = rle_enc1(tmp, c, pre); + n_bytes2 += rle_enc1(tmp + n_bytes2, a, rl); + n_bytes2 += rle_enc1(tmp + n_bytes2, c, l - pre); + } + if (n_bytes != n_bytes2 && end != p + n_bytes) // size changed + memmove(p + n_bytes2, p + n_bytes, end - p - n_bytes); + memcpy(p, tmp, n_bytes2); + diff = n_bytes2 - n_bytes; + } + return (*nptr += diff); +} + +int rle_insert(uint8_t *block, int64_t x, int a, int64_t rl, int64_t cnt[6], const int64_t ec[6]) +{ + int beg = 0; + int64_t bc[6]; + memset(bc, 0, 48); + return rle_insert_cached(block, x, a, rl, cnt, ec, &beg, bc); +} + +void rle_split(uint8_t *block, uint8_t *new_block) +{ + int n = *(uint16_t*)block; + uint8_t *end = block + 2 + n, *q = block + 2 + (n>>1); + while (*q>>6 == 2) --q; + memcpy(new_block + 2, q, end - q); + *(uint16_t*)new_block = end - q; + *(uint16_t*)block = q - block - 2; +} + +void rle_count(const uint8_t *block, int64_t cnt[6]) +{ + const uint8_t *q = block + 2, *end = q + *(uint16_t*)block; + while (q < end) { + int c; + int64_t l; + rle_dec1(q, c, l); + cnt[c] += l; + } +} + +void rle_print(const uint8_t *block, int expand) +{ + const uint16_t *p = (const uint16_t*)block; + const uint8_t *q = block + 2, *end = block + 2 + *p; + while (q < end) { + int c; + int64_t l, x; + rle_dec1(q, c, l); + if (expand) for (x = 0; x < l; ++x) putchar("$ACGTN"[c]); + else printf("%c%ld", "$ACGTN"[c], (long)l); + } + putchar('\n'); +} + +void rle_rank2a(const uint8_t *block, int64_t x, int64_t y, int64_t *cx, int64_t *cy, const int64_t ec[6]) +{ + int a; + int64_t tot, cnt[6]; + const uint8_t *p; + + y = y >= x? y : x; + tot = ec[0] + ec[1] + ec[2] + ec[3] + ec[4] + ec[5]; + if (tot == 0) return; + if (x <= (tot - y) + (tot>>3)) { + int c = 0; + int64_t l, z = 0; + memset(cnt, 0, 48); + p = block + 2; + while (z < x) { + rle_dec1(p, c, l); + z += l; cnt[c] += l; + } + for (a = 0; a != 6; ++a) cx[a] += cnt[a]; + cx[c] -= z - x; + if (cy) { + while (z < y) { + rle_dec1(p, c, l); + z += l; cnt[c] += l; + } + for (a = 0; a != 6; ++a) cy[a] += cnt[a]; + cy[c] -= z - y; + } + } else { +#define move_backward(_x) \ + while (z >= (_x)) { \ + --p; \ + if (*p>>6 != 2) { \ + l |= *p>>7? (int64_t)rle_auxtab[*p>>3&7]>>4 << t : *p>>3; \ + z -= l; cnt[*p&7] -= l; \ + l = 0; t = 0; \ + } else { \ + l |= (*p&0x3fL) << t; \ + t += 6; \ + } \ + } \ + + int t = 0; + int64_t l = 0, z = tot; + memcpy(cnt, ec, 48); + p = block + 2 + *(const uint16_t*)block; + if (cy) { + move_backward(y) + for (a = 0; a != 6; ++a) cy[a] += cnt[a]; + cy[*p&7] += y - z; + } + move_backward(x) + for (a = 0; a != 6; ++a) cx[a] += cnt[a]; + cx[*p&7] += x - z; + +#undef move_backward + } +} diff --git a/rle.h b/rle.h new file mode 100644 index 0000000..0d59484 --- /dev/null +++ b/rle.h @@ -0,0 +1,77 @@ +#ifndef RLE6_H_ +#define RLE6_H_ + +#include + +#ifdef __GNUC__ +#define LIKELY(x) __builtin_expect((x),1) +#else +#define LIKELY(x) (x) +#endif +#ifdef __cplusplus + +extern "C" { +#endif + + int rle_insert_cached(uint8_t *block, int64_t x, int a, int64_t rl, int64_t cnt[6], const int64_t ec[6], int *beg, int64_t bc[6]); + int rle_insert(uint8_t *block, int64_t x, int a, int64_t rl, int64_t cnt[6], const int64_t end_cnt[6]); + void rle_split(uint8_t *block, uint8_t *new_block); + void rle_count(const uint8_t *block, int64_t cnt[6]); + void rle_rank2a(const uint8_t *block, int64_t x, int64_t y, int64_t *cx, int64_t *cy, const int64_t ec[6]); + #define rle_rank1a(block, x, cx, ec) rle_rank2a(block, x, -1, cx, 0, ec) + + void rle_print(const uint8_t *block, int expand); + +#ifdef __cplusplus +} +#endif + +/****************** + *** 43+3 codec *** + ******************/ + +const uint8_t rle_auxtab[8]; + +#define RLE_MIN_SPACE 18 +#define rle_nptr(block) ((uint16_t*)(block)) + +// decode one run (c,l) and move the pointer p +#define rle_dec1(p, c, l) do { \ + (c) = *(p) & 7; \ + if (LIKELY((*(p)&0x80) == 0)) { \ + (l) = *(p)++ >> 3; \ + } else if (LIKELY(*(p)>>5 == 6)) { \ + (l) = (*(p)&0x18L)<<3L | ((p)[1]&0x3fL); \ + (p) += 2; \ + } else { \ + int n = ((*(p)&0x10) >> 2) + 4; \ + (l) = *(p)++ >> 3 & 1; \ + while (--n) (l) = ((l)<<6) | (*(p)++&0x3fL); \ + } \ + } while (0) + +static inline int rle_enc1(uint8_t *p, int c, int64_t l) +{ + if (l < 1LL<<4) { + *p = l << 3 | c; + return 1; + } else if (l < 1LL<<8) { + *p = 0xC0 | l >> 6 << 3 | c; + p[1] = 0x80 | (l & 0x3f); + return 2; + } else if (l < 1LL<<19) { + *p = 0xE0 | l >> 18 << 3 | c; + p[1] = 0x80 | (l >> 12 & 0x3f); + p[2] = 0x80 | (l >> 6 & 0x3f); + p[3] = 0x80 | (l & 0x3f); + return 4; + } else { + int i, shift = 36; + *p = 0xF0 | l >> 42 << 3 | c; + for (i = 1; i < 8; ++i, shift -= 6) + p[i] = 0x80 | (l>>shift & 0x3f); + return 8; + } +} + +#endif diff --git a/rope.c b/rope.c new file mode 100644 index 0000000..8d21a06 --- /dev/null +++ b/rope.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include "rle.h" +#include "rope.h" + +/******************* + *** Memory Pool *** + *******************/ + +#define MP_CHUNK_SIZE 0x100000 // 1MB per chunk + +typedef struct { // memory pool for fast and compact memory allocation (no free) + int size, i, n_elems; + int64_t top, max; + uint8_t **mem; +} mempool_t; + +static mempool_t *mp_init(int size) +{ + mempool_t *mp; + mp = calloc(1, sizeof(mempool_t)); + mp->size = size; + mp->i = mp->n_elems = MP_CHUNK_SIZE / size; + mp->top = -1; + return mp; +} + +static void mp_destroy(mempool_t *mp) +{ + int64_t i; + for (i = 0; i <= mp->top; ++i) free(mp->mem[i]); + free(mp->mem); free(mp); +} + +static inline void *mp_alloc(mempool_t *mp) +{ + if (mp->i == mp->n_elems) { + if (++mp->top == mp->max) { + mp->max = mp->max? mp->max<<1 : 1; + mp->mem = realloc(mp->mem, sizeof(void*) * mp->max); + } + mp->mem[mp->top] = calloc(mp->n_elems, mp->size); + mp->i = 0; + } + return mp->mem[mp->top] + (mp->i++) * mp->size; +} + +/*************** + *** B+ rope *** + ***************/ + +rope_t *rope_init(int max_nodes, int block_len) +{ + rope_t *rope; + rope = calloc(1, sizeof(rope_t)); + if (block_len < 32) block_len = 32; + rope->max_nodes = (max_nodes+ 1)>>1<<1; + rope->block_len = (block_len + 7) >> 3 << 3; + rope->node = mp_init(sizeof(rpnode_t) * rope->max_nodes); + rope->leaf = mp_init(rope->block_len); + rope->root = mp_alloc(rope->node); + rope->root->n = 1; + rope->root->is_bottom = 1; + rope->root->p = mp_alloc(rope->leaf); + return rope; +} + +void rope_destroy(rope_t *rope) +{ + mp_destroy(rope->node); + mp_destroy(rope->leaf); + free(rope); +} + +static inline rpnode_t *split_node(rope_t *rope, rpnode_t *u, rpnode_t *v) +{ // split $v's child. $u is the first node in the bucket. $v and $u are in the same bucket. IMPORTANT: there is always enough room in $u + int j, i = v - u; + rpnode_t *w; // $w is the sibling of $v + if (u == 0) { // only happens at the root; add a new root + u = v = mp_alloc(rope->node); + v->n = 1; v->p = rope->root; // the new root has the old root as the only child + memcpy(v->c, rope->c, 48); + for (j = 0; j < 6; ++j) v->l += v->c[j]; + rope->root = v; + } + if (i != u->n - 1) // then make room for a new node + memmove(v + 2, v + 1, sizeof(rpnode_t) * (u->n - i - 1)); + ++u->n; w = v + 1; + memset(w, 0, sizeof(rpnode_t)); + w->p = mp_alloc(u->is_bottom? rope->leaf : rope->node); + if (u->is_bottom) { // we are at the bottom level; $v->p is a string instead of a node + uint8_t *p = (uint8_t*)v->p, *q = (uint8_t*)w->p; + rle_split(p, q); + rle_count(q, w->c); + } else { // $v->p is a node, not a string + rpnode_t *p = v->p, *q = w->p; // $v and $w are siblings and thus $p and $q are cousins + p->n -= rope->max_nodes>>1; + memcpy(q, p + p->n, sizeof(rpnode_t) * (rope->max_nodes>>1)); + q->n = rope->max_nodes>>1; // NB: this line must below memcpy() as $q->n and $q->is_bottom are modified by memcpy() + q->is_bottom = p->is_bottom; + for (i = 0; i < q->n; ++i) + for (j = 0; j < 6; ++j) + w->c[j] += q[i].c[j]; + } + for (j = 0; j < 6; ++j) // compute $w->l and update $v->c + w->l += w->c[j], v->c[j] -= w->c[j]; + v->l -= w->l; // update $v->c + return v; +} + +int64_t rope_insert_run(rope_t *rope, int64_t x, int a, int64_t rl, rpcache_t *cache) +{ // insert $a after $x symbols in $rope and the returns rank(a, x) + rpnode_t *u = 0, *v = 0, *p = rope->root; // $v is the parent of $p; $u and $v are at the same level and $u is the first node in the bucket + int64_t y = 0, z = 0, cnt[6]; + int n_runs; + do { // top-down update. Searching and node splitting are done together in one pass. + if (p->n == rope->max_nodes) { // node is full; split + v = split_node(rope, u, v); // $v points to the parent of $p; when a new root is added, $v points to the root + if (y + v->l < x) // if $v is not long enough after the split, we need to move both $p and its parent $v + y += v->l, z += v->c[a], ++v, p = v->p; + } + u = p; + if (v && x - y > v->l>>1) { // then search backwardly for the right node to descend + p += p->n - 1; y += v->l; z += v->c[a]; + for (; y >= x; --p) y -= p->l, z -= p->c[a]; + ++p; + } else for (; y + p->l < x; ++p) y += p->l, z += p->c[a]; // then search forwardly + assert(p - u < u->n); + if (v) v->c[a] += rl, v->l += rl; // we should not change p->c[a] because this may cause troubles when p's child is split + v = p; p = p->p; // descend + } while (!u->is_bottom); + rope->c[a] += rl; // $rope->c should be updated after the loop as adding a new root needs the old $rope->c counts + if (cache) { + if (cache->p != (uint8_t*)p) memset(cache, 0, sizeof(rpcache_t)); + n_runs = rle_insert_cached((uint8_t*)p, x - y, a, rl, cnt, v->c, &cache->beg, cache->bc); + cache->p = (uint8_t*)p; + } else n_runs = rle_insert((uint8_t*)p, x - y, a, rl, cnt, v->c); + z += cnt[a]; + v->c[a] += rl; v->l += rl; // this should be after rle_insert(); otherwise rle_insert() won't work + if (n_runs + RLE_MIN_SPACE > rope->block_len) { + split_node(rope, u, v); + if (cache) memset(cache, 0, sizeof(rpcache_t)); + } + return z; +} + +static rpnode_t *rope_count_to_leaf(const rope_t *rope, int64_t x, int64_t cx[6], int64_t *rest) +{ + rpnode_t *u, *v = 0, *p = rope->root; + int64_t y = 0; + int a; + + memset(cx, 0, 48); + do { + u = p; + if (v && x - y > v->l>>1) { + p += p->n - 1; y += v->l; + for (a = 0; a != 6; ++a) cx[a] += v->c[a]; + for (; y >= x; --p) { + y -= p->l; + for (a = 0; a != 6; ++a) cx[a] -= p->c[a]; + } + ++p; + } else { + for (; y + p->l < x; ++p) { + y += p->l; + for (a = 0; a != 6; ++a) cx[a] += p->c[a]; + } + } + v = p; p = p->p; + } while (!u->is_bottom); + *rest = x - y; + return v; +} + +void rope_rank2a(const rope_t *rope, int64_t x, int64_t y, int64_t *cx, int64_t *cy) +{ + rpnode_t *v; + int64_t rest; + v = rope_count_to_leaf(rope, x, cx, &rest); + if (y < x || cy == 0) { + rle_rank1a((const uint8_t*)v->p, rest, cx, v->c); + } else if (rest + (y - x) <= v->l) { + memcpy(cy, cx, 48); + rle_rank2a((const uint8_t*)v->p, rest, rest + (y - x), cx, cy, v->c); + } else { + rle_rank1a((const uint8_t*)v->p, rest, cx, v->c); + v = rope_count_to_leaf(rope, y, cy, &rest); + rle_rank1a((const uint8_t*)v->p, rest, cy, v->c); + } +} + +/********************* + *** Rope iterator *** + *********************/ + +void rope_itr_first(const rope_t *rope, rpitr_t *i) +{ + memset(i, 0, sizeof(rpitr_t)); + i->rope = rope; + for (i->pa[i->d] = rope->root; !i->pa[i->d]->is_bottom;) // descend to the leftmost leaf + ++i->d, i->pa[i->d] = i->pa[i->d - 1]->p; +} + +const uint8_t *rope_itr_next_block(rpitr_t *i) +{ + const uint8_t *ret; + assert(i->d < ROPE_MAX_DEPTH); // a B+ tree should not be that tall + if (i->d < 0) return 0; + ret = (uint8_t*)i->pa[i->d][i->ia[i->d]].p; + while (i->d >= 0 && ++i->ia[i->d] == i->pa[i->d]->n) i->ia[i->d--] = 0; // backtracking + if (i->d >= 0) + while (!i->pa[i->d]->is_bottom) // descend to the leftmost leaf + ++i->d, i->pa[i->d] = i->pa[i->d - 1][i->ia[i->d - 1]].p; + return ret; +} diff --git a/rope.h b/rope.h new file mode 100644 index 0000000..d114713 --- /dev/null +++ b/rope.h @@ -0,0 +1,54 @@ +#ifndef ROPE_H_ +#define ROPE_H_ + +#include +#include + +#define ROPE_MAX_DEPTH 80 +#define ROPE_DEF_MAX_NODES 64 +#define ROPE_DEF_BLOCK_LEN 512 + +typedef struct rpnode_s { + struct rpnode_s *p; // child; at the bottom level, $p points to a string with the first 2 bytes giving the number of runs (#runs) + uint64_t l:54, n:9, is_bottom:1; // $n and $is_bottom are only set for the first node in a bucket + int64_t c[6]; // marginal counts +} rpnode_t; + +typedef struct { + int32_t max_nodes, block_len; // both MUST BE even numbers + int64_t c[6]; // marginal counts + rpnode_t *root; + void *node, *leaf; // memory pool +} rope_t; + +typedef struct { + const rope_t *rope; // the rope + const rpnode_t *pa[ROPE_MAX_DEPTH]; // parent nodes + int ia[ROPE_MAX_DEPTH]; // index in the parent nodes + int d; // the current depth in the B+-tree +} rpitr_t; + +typedef struct { + int beg; + int64_t bc[6]; + uint8_t *p; +} rpcache_t; + +#ifdef __cplusplus +extern "C" { +#endif + + rope_t *rope_init(int max_nodes, int block_len); + void rope_destroy(rope_t *rope); + int64_t rope_insert_run(rope_t *rope, int64_t x, int a, int64_t rl, rpcache_t *cache); + void rope_rank2a(const rope_t *rope, int64_t x, int64_t y, int64_t *cx, int64_t *cy); + #define rope_rank1a(rope, x, cx) rope_rank2a(rope, x, -1, cx, 0) + + void rope_itr_first(const rope_t *rope, rpitr_t *i); + const uint8_t *rope_itr_next_block(rpitr_t *i); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/MT-simu.fq.gz b/test/MT-simu.fq.gz new file mode 100644 index 0000000000000000000000000000000000000000..8d0558aa75f2c50ef7e41696387ad2d100959704 GIT binary patch literal 185887 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 AnE(I) literal 0 HcmV?d00001 diff --git a/unitig.c b/unitig.c new file mode 100644 index 0000000..55e2fd9 --- /dev/null +++ b/unitig.c @@ -0,0 +1,455 @@ +#include +#include +#include +#include "kvec.h" +#include "kstring.h" +#include "rld0.h" +#include "mag.h" +#include "internal.h" + +/****************** + *** From fermi *** + ******************/ + +typedef struct { size_t n, m; int32_t *a; } fm32s_v; +typedef struct { size_t n, m; rldintv_t *a; } rldintv_v; + +static uint64_t utg_primes[] = { 123457, 234571, 345679, 456791, 567899, 0 }; + +#define fm6_comp(a) ((a) >= 1 && (a) <= 4? 5 - (a) : (a)) +#define fm6_set_intv(e, c, ik) ((ik).x[0] = (e)->cnt[(int)(c)], (ik).x[2] = (e)->cnt[(int)(c)+1] - (e)->cnt[(int)(c)], (ik).x[1] = (e)->cnt[fm6_comp(c)], (ik).info = 0) + +int rld_extend0(const rld_t *e, const rldintv_t *ik, rldintv_t *ok0, int is_back) +{ // FIXME: this can be accelerated a little by using rld_rank1a() when ik.x[2]==1 + uint64_t tk[6], tl[6]; + rld_rank2a(e, ik->x[!is_back], ik->x[!is_back] + ik->x[2], tk, tl); + ok0->x[!is_back] = tk[0]; + ok0->x[is_back] = ik->x[is_back]; + ok0->x[2] = tl[0] - tk[0]; + return 0; +} + +uint64_t fm6_retrieve(const rld_t *e, uint64_t x, kstring_t *s, rldintv_t *k2, int *contained) +{ + uint64_t k = x, ok[6]; + rldintv_t ok2[6]; + s->l = 0; *contained = 0; + while (1) { + int c = rld_rank1a(e, k + 1, ok); + k = e->cnt[c] + ok[c] - 1; + if (c == 0) break; + if (s->l > 0) { + if (k2->x[2] == 1) k2->x[0] = k; + else { + rld_extend(e, k2, ok2, 1); + *k2 = ok2[c]; + } + } else fm6_set_intv(e, c, *k2); + kputc(c, s); + } + if (k2->x[2] != 1) { + rld_extend(e, k2, ok2, 1); + if (ok2[0].x[2] != k2->x[2]) *contained |= 1; // left contained + *k2 = ok2[0]; + } else k2->x[0] = k; + rld_extend(e, k2, ok2, 0); + if (ok2[0].x[2] != k2->x[2]) *contained |= 2; // right contained + *k2 = ok2[0]; + return k; +} + +/***************** + *** Main body *** + *****************/ + +#define info_lt(a, b) ((a).info < (b).info) + +#include "ksort.h" +KSORT_INIT(infocmp, rldintv_t, info_lt) + +static inline void set_bit(uint64_t *bits, uint64_t x) +{ + uint64_t *p = bits + (x>>6); + uint64_t z = 1LLU<<(x&0x3f); + __sync_fetch_and_or(p, z); +} + +static inline void set_bits(uint64_t *bits, const rldintv_t *p) +{ + uint64_t k; + for (k = 0; k < p->x[2]; ++k) { + set_bit(bits, p->x[0] + k); + set_bit(bits, p->x[1] + k); + } +} + +static rldintv_t overlap_intv(const rld_t *e, int len, const uint8_t *seq, int min, int j, int at5, rldintv_v *p, int inc_sentinel) +{ // requirement: seq[j] matches the end of a read + int c, depth, dir, end; + rldintv_t ik, ok[6]; + p->n = 0; + dir = at5? 1 : -1; // at5 is true iff we start from the 5'-end of a read + end = at5? len : -1; + c = seq[j]; + fm6_set_intv(e, c, ik); + for (depth = 1, j += dir; j != end; j += dir, ++depth) { + c = at5? fm6_comp(seq[j]) : seq[j]; + rld_extend(e, &ik, ok, !at5); + if (!ok[c].x[2]) break; // cannot be extended + if (depth >= min && ok[0].x[2]) { + if (inc_sentinel) { + ok[0].info = j - dir; + kv_push(rldintv_t, *p, ok[0]); + } else { + ik.info = j - dir; + kv_push(rldintv_t, *p, ik); + } + } + ik = ok[c]; + } + kv_reverse(rldintv_t, *p, 0); // reverse the array such that the smallest interval comes first + return ik; +} + +typedef struct { + const rld_t *e; + int min_match, min_merge_len; + rldintv_v a[2], nei; + fm32s_v cat; + uint64_t *used, *bend; + kstring_t str; + uint64_t n, sum, sum2, unpaired; +} aux_t; + +int fm6_is_contained(const rld_t *e, int min_match, const kstring_t *s, rldintv_t *intv, rldintv_v *ovlp) +{ // for s is a sequence in e, test if s is contained in other sequences in e; return intervals right overlapping with s + rldintv_t ik, ok[6]; + int ret = 0; + assert(s->l > min_match); + ovlp->n = 0; + ik = overlap_intv(e, s->l, (uint8_t*)s->s, min_match, s->l - 1, 0, ovlp, 0); + rld_extend(e, &ik, ok, 1); assert(ok[0].x[2]); + if (ik.x[2] != ok[0].x[2]) ret = -1; // the sequence is left contained + ik = ok[0]; + rld_extend(e, &ik, ok, 0); assert(ok[0].x[2]); + if (ik.x[2] != ok[0].x[2]) ret = -1; // the sequence is right contained + *intv = ok[0]; + return ret; +} + +int fm6_get_nei(const rld_t *e, int min_match, int beg, kstring_t *s, rldintv_v *nei, // input and output variables + rldintv_v *prev, rldintv_v *curr, fm32s_v *cat, // temporary arrays + uint64_t *used) // optional info +{ + int ori_l = s->l, j, i, c, rbeg, is_forked = 0; + rldintv_v *swap; + rldintv_t ok[6], ok0; + + curr->n = nei->n = cat->n = 0; + if (prev->n == 0) { // when this routine is called for the seed, prev may filled by fm6_is_contained() + overlap_intv(e, s->l - beg, (uint8_t*)s->s + beg, min_match, s->l - beg - 1, 0, prev, 0); + if (prev->n == 0) return -1; // no overlap + for (j = 0; j < prev->n; ++j) prev->a[j].info += beg; + } + kv_resize(int, *cat, prev->m); + for (j = 0; j < prev->n; ++j) cat->a[j] = 0; // only one interval; all point to 0 + while (prev->n) { + for (j = 0, curr->n = 0; j < prev->n; ++j) { + rldintv_t *p = &prev->a[j]; + if (cat->a[j] < 0) continue; + rld_extend(e, p, ok, 0); // forward extension + if (ok[0].x[2] && ori_l != s->l) { // some (partial) reads end here + rld_extend0(e, &ok[0], &ok0, 1); // backward extension to look for sentinels + if (ok0.x[2]) { // the match is bounded by sentinels - a full-length match + if (ok[0].x[2] == p->x[2] && p->x[2] == ok0.x[2]) { // never consider a read contained in another read + int cat0 = cat->a[j]; // a category approximately corresponds to one neighbor, though not always + assert(j == 0 || cat->a[j] > cat->a[j-1]); // otherwise not irreducible + ok0.info = ori_l - (p->info&0xffffffffU); + for (i = j; i < prev->n && cat->a[i] == cat0; ++i) cat->a[i] = -1; // mask out other intervals of the same cat + kv_push(rldintv_t, *nei, ok0); // keep in the neighbor vector + continue; // no need to go through for(c); do NOT set "used" as this neighbor may be rejected later + } else if (used) set_bits(used, &ok0); // the read is contained in another read; mark it as used + } + } // ~if(ok[0].x[2]) + if (cat->a[j] < 0) continue; // no need to proceed if we have finished this path + for (c = 1; c < 5; ++c) // collect extensible intervals + if (ok[c].x[2]) { + rld_extend0(e, &ok[c], &ok0, 1); + if (ok0.x[2]) { // do not extend intervals whose left end is not bounded by a sentinel + ok[c].info = (p->info&0xfffffff0ffffffffLLU) | (uint64_t)c<<32; + kv_push(rldintv_t, *curr, ok[c]); + } + } + } // ~for(j) + if (curr->n) { // update category + uint32_t last, cat0; + kv_resize(int, *cat, curr->m); + c = curr->a[0].info>>32&0xf; + kputc(fm6_comp(c), s); + ks_introsort(infocmp, curr->n, curr->a); + last = curr->a[0].info >> 32; + cat->a[0] = 0; + curr->a[0].info &= 0xffffffff; + for (j = 1, cat0 = 0; j < curr->n; ++j) { // this loop recalculate cat + if (curr->a[j].info>>32 != last) + last = curr->a[j].info>>32, cat0 = j; + cat->a[j] = cat0; + curr->a[j].info = (curr->a[j].info&0xffffffff) | (uint64_t)cat0<<36; + } + if (cat0 != 0) is_forked = 1; + } + swap = curr; curr = prev; prev = swap; // swap curr and prev + } // ~while(prev->n) + if (nei->n == 0) return -1; // no overlap + rbeg = ori_l - (uint32_t)nei->a[0].info; + if (nei->n == 1 && is_forked) { // this may happen if there are contained reads; fix this + fm6_set_intv(e, 0, ok0); + for (i = rbeg; i < ori_l; ++i) { + rld_extend(e, &ok0, ok, 0); + ok0 = ok[fm6_comp(s->s[i])]; + } + for (i = ori_l; i < s->l; ++i) { + int c0 = -1; + rld_extend(e, &ok0, ok, 0); + for (c = 1, j = 0; c < 5; ++c) + if (ok[c].x[2] && ok[c].x[0] <= nei->a[0].x[0] && ok[c].x[0] + ok[c].x[2] >= nei->a[0].x[0] + nei->a[0].x[2]) + ++j, c0 = c; + if (j == 0 && ok[0].x[2]) break; + assert(j == 1); + s->s[i] = fm6_comp(c0); + ok0 = ok[c0]; + } + s->l = i; s->s[s->l] = 0; + } + if (nei->n > 1) s->l = ori_l, s->s[s->l] = 0; + return rbeg; +} + +static int try_right(aux_t *a, int beg, kstring_t *s) +{ + return fm6_get_nei(a->e, a->min_match, beg, s, &a->nei, &a->a[0], &a->a[1], &a->cat, a->used); +} + +static int check_left_simple(aux_t *a, int beg, int rbeg, const kstring_t *s) +{ + rldintv_t ok[6]; + rldintv_v *prev = &a->a[0], *curr = &a->a[1], *swap; + int i, j; + + overlap_intv(a->e, s->l, (uint8_t*)s->s, a->min_match, rbeg, 1, prev, 1); + for (i = rbeg - 1; i >= beg; --i) { + for (j = 0, curr->n = 0; j < prev->n; ++j) { + rldintv_t *p = &prev->a[j]; + rld_extend(a->e, p, ok, 1); + if (ok[0].x[2]) set_bits(a->used, &ok[0]); // some reads end here; they must be contained in a longer read + if (ok[0].x[2] + ok[(int)s->s[i]].x[2] != p->x[2]) return -1; // potential backward bifurcation + kv_push(rldintv_t, *curr, ok[(int)s->s[i]]); + } + swap = curr; curr = prev; prev = swap; + } // ~for(i) + return 0; +} + +static int check_left(aux_t *a, int beg, int rbeg, const kstring_t *s) +{ + int i, ret; + rldintv_t tmp; + assert(a->nei.n == 1); + ret = check_left_simple(a, beg, rbeg, s); + if (ret == 0) return 0; + // when ret<0, the back fork may be caused by a contained read. we have to do more to confirm this. + tmp = a->nei.a[0]; // backup the neighbour as it will be overwritten by try_right() + a->a[0].n = a->a[1].n = a->nei.n = 0; + ks_resize(&a->str, s->l - rbeg + 1); + for (i = s->l - 1, a->str.l = 0; i >= rbeg; --i) + a->str.s[a->str.l++] = fm6_comp(s->s[i]); + a->str.s[a->str.l] = 0; + try_right(a, 0, &a->str); + assert(a->nei.n >= 1); + ret = a->nei.n > 1? -1 : 0; + a->nei.n = 1; a->nei.a[0] = tmp; // recover the original neighbour + return ret; +} + +static int unitig_unidir(aux_t *a, kstring_t *s, kstring_t *cov, int beg0, uint64_t k0, uint64_t *end, int *is_loop) +{ + int i, beg = beg0, rbeg, ori_l = s->l, n_reads = 0; + *is_loop = 0; + while ((rbeg = try_right(a, beg, s)) >= 0) { // loop if there is at least one overlap + uint64_t k; + if (a->nei.n > 1) { // forward bifurcation + set_bit(a->bend, *end); + break; + } + if ((k = a->nei.a[0].x[0]) == *end) break; // a loop like b>>c>>a>bend[k>>6]>>(k&0x3f)&1) || check_left(a, beg, rbeg, s) < 0)) { // backward bifurcation + set_bit(a->bend, k); + break; + } + if (k == k0) { // a loop like a>>b>>c>>a + *is_loop = 1; + break; + } + if (a->nei.a[0].x[1] == *end) { // a loop like b>>c>>a>>a; cut the last link + a->nei.n = 0; + break; + } + if ((int)a->nei.a[0].info < a->min_merge_len) break; // the overlap is not long enough + *end = a->nei.a[0].x[1]; + set_bits(a->used, &a->nei.a[0]); // successful extension + ++n_reads; + if (cov->m < s->m) ks_resize(cov, s->m); + cov->l = s->l; cov->s[cov->l] = 0; + for (i = rbeg; i < ori_l; ++i) // update the coverage string + if (cov->s[i] != '~') ++cov->s[i]; + for (i = ori_l; i < s->l; ++i) cov->s[i] = '"'; + beg = rbeg; ori_l = s->l; a->a[0].n = a->a[1].n = 0; // prepare for the next round of loop + } + cov->l = s->l = ori_l; s->s[ori_l] = cov->s[ori_l] = 0; + return n_reads; +} + +static void copy_nei(ku128_v *dst, const rldintv_v *src) +{ + int i; + for (i = 0; i < src->n; ++i) { + ku128_t z; + z.x = src->a[i].x[0]; z.y = src->a[i].info; + kv_push(ku128_t, *dst, z); + } +} + +static int unitig1(aux_t *a, int64_t seed, kstring_t *s, kstring_t *cov, uint64_t end[2], ku128_v nei[2], int *n_reads) +{ + rldintv_t intv0; + int seed_len, ret, is_loop, contained; + int64_t k; + size_t i; + + *n_reads = nei[0].n = nei[1].n = 0; + if (a->used[seed>>6]>>(seed&0x3f)&1) return -2; // used + // retrieve the sequence pointed by seed + k = fm6_retrieve(a->e, seed, s, &intv0, &contained); + seq_reverse(s->l, (uint8_t*)s->s); + seed_len = s->l; + // check contained status + if (intv0.x[2] > 1 && k != intv0.x[0]) return -3; // duplicated, but not the first + set_bits(a->used, &intv0); + if (contained) return -3; // contained + // check length, containment and if used before + if (s->l <= a->min_match) return -1; // too short + ret = fm6_is_contained(a->e, a->min_match, s, &intv0, &a->a[0]); + *n_reads = 1; + // initialize the coverage string + if (cov->m < s->m) ks_resize(cov, s->m); + cov->l = s->l; cov->s[cov->l] = 0; + for (i = 0; i < cov->l; ++i) cov->s[i] = '"'; + // left-wards extension + end[0] = intv0.x[1]; end[1] = intv0.x[0]; + if (a->a[0].n) { // no need to extend to the right if there is no overlap + *n_reads += unitig_unidir(a, s, cov, 0, intv0.x[0], &end[0], &is_loop); + copy_nei(&nei[0], &a->nei); + if (is_loop) { + ku128_t z; + z.x = end[0]; z.y = a->nei.a[0].info; + kv_push(ku128_t, nei[1], z); + return 0; + } + } + // right-wards extension + a->a[0].n = a->a[1].n = a->nei.n = 0; + seq_revcomp6(s->l, (uint8_t*)s->s); // reverse complement for extension in the other direction + seq_reverse(cov->l, (uint8_t*)cov->s); // reverse the coverage + *n_reads += unitig_unidir(a, s, cov, s->l - seed_len, intv0.x[1], &end[1], &is_loop); + copy_nei(&nei[1], &a->nei); + return 0; +} + +typedef struct { + long max_l; + aux_t a; + kstring_t str, cov; + magv_t z; + magv_v v; +} thrdat_t; + +typedef struct { + uint64_t prime, *used, *bend, *visited; + const rld_t *e; + thrdat_t *d; +} worker_t; + +static void worker(void *data, long _i, int tid) +{ + worker_t *w = (worker_t*)data; + thrdat_t *d = &w->d[tid]; + uint64_t i = (w->prime * _i) % w->e->mcnt[1]; + if (unitig1(&d->a, i, &d->str, &d->cov, d->z.k, d->z.nei, &d->z.nsr) >= 0) { // then we keep the unitig + uint64_t *p[2], x[2]; + magv_t *q; + p[0] = w->visited + (d->z.k[0]>>6); x[0] = 1LLU<<(d->z.k[0]&0x3f); + p[1] = w->visited + (d->z.k[1]>>6); x[1] = 1LLU<<(d->z.k[1]&0x3f); + if ((__sync_fetch_and_or(p[0], x[0])&x[0]) || (__sync_fetch_and_or(p[1], x[1])&x[1])) return; + d->z.len = d->str.l; + if (d->max_l < d->str.m) { + d->max_l = d->str.m; + d->z.seq = realloc(d->z.seq, d->max_l); + d->z.cov = realloc(d->z.cov, d->max_l); + } + memcpy(d->z.seq, d->str.s, d->z.len); + memcpy(d->z.cov, d->cov.s, d->z.len + 1); + kv_pushp(magv_t, d->v, &q); + mag_v_copy_to_empty(q, &d->z); + } +} + +mag_t *fml_fmi2mag_core(const rld_t *e, int min_match, int min_merge_len, int n_threads) +{ + extern void kt_for(int n_threads, void (*func)(void*,long,int), void *data, long n); + worker_t w; + int j; + mag_t *g; + + w.used = (uint64_t*)calloc((e->mcnt[1] + 63)/64, 8); + w.bend = (uint64_t*)calloc((e->mcnt[1] + 63)/64, 8); + w.visited = (uint64_t*)calloc((e->mcnt[1] + 63)/64, 8); + w.e = e; + assert(e->mcnt[1] >= n_threads * 2); + w.d = calloc(n_threads, sizeof(thrdat_t)); + w.prime = 0; + for (j = 0; utg_primes[j] > 0; ++j) + if (e->mcnt[1] % utg_primes[j] != 0) { + w.prime = utg_primes[j]; + break; + } + assert(w.prime); + for (j = 0; j < n_threads; ++j) { + w.d[j].a.e = e; w.d[j].a.min_match = min_match; w.d[j].a.min_merge_len = min_merge_len; + w.d[j].a.used = w.used; w.d[j].a.bend = w.bend; + } + kt_for(n_threads, worker, &w, e->mcnt[1]); + g = (mag_t*)calloc(1, sizeof(mag_t)); + for (j = 0; j < n_threads; ++j) { + kv_resize(magv_t, g->v, g->v.n + w.d[j].v.n); + memcpy(g->v.a + g->v.n, w.d[j].v.a, w.d[j].v.n * sizeof(magv_t)); + g->v.n += w.d[j].v.n; + free(w.d[j].v.a); + free(w.d[j].a.a[0].a); free(w.d[j].a.a[1].a); free(w.d[j].a.nei.a); free(w.d[j].a.cat.a); + free(w.d[j].z.nei[0].a); free(w.d[j].z.nei[1].a); free(w.d[j].z.seq); free(w.d[j].z.cov); + free(w.d[j].a.str.s); free(w.d[j].str.s); free(w.d[j].cov.s); + } + free(w.d); free(w.used); free(w.bend); free(w.visited); + + mag_g_build_hash(g); + mag_g_amend(g); + g->rdist = mag_cal_rdist(g); + return g; +} + +mag_t *fml_fmi2mag(const fml_opt_t *opt, rld_t *e) +{ + mag_t *g; + g = fml_fmi2mag_core(e, opt->min_asm_ovlp, opt->min_merge_len, opt->n_threads); + rld_destroy(e); + return g; +} -- 2.30.2